C++ 元编程中的蹦床
Trampolines in C++ Metaprogramming
我正在读一本关于元编程的书,在 Trampolines 上有分裂:
struct generic_t
{
void* obj;
void(*del)(void*);
};
template <typename T> // outer template parameter
generic_t copy_to_generic(const T& value)
{
struct local_cast // local class
{
static void destroy(void* p) // void*-based interface
{
delete static_cast<T*>(p); // static type knowledge
}
};
generic_t p;
p.obj = new T(value); // information loss: copy T* to void*
p.del = &local_cast::destroy;
return p;
}
我完全明白它是如何工作的,但我不知道它的应用是什么!你通常在哪里使用这种技术?有人知道吗?谢谢:)
我在我的程序中的很多地方都用到了它。我喜欢这种方法的一件事是您可以保存一个不相关类型的列表。例如,我见过很多这样的代码:
struct Abstract { virtual ~Abstract() = default; };
template<typename P>
struct AbstractHandler : Abstract {
virtual void handle(P) = 0;
};
template<typename P, typename H>
struct Handler : AbstractHandler<P>, private H {
void handle(P p) override {
H::handle(p);
}
};
struct Test1 {};
struct Test1Handler {
void handle(Test1) {}
};
struct Test2 {};
struct Test2Handler {
void handle(Test2) {}
};
int main() {
std::vector<std::unique_ptr<Abstract>> handlers;
handlers.emplace_back(std::make_unique<Handler<Test1, Test1Handler>>());
handlers.emplace_back(std::make_unique<Handler<Test2, Test2Handler>>());
// some code later....
dynamic_cast<AbstractHandler<Test1>*>(handlers[0].get())->handle(Test1{});
dynamic_cast<AbstractHandler<Test2>*>(handlers[1].get())->handle(Test2{});
}
动态转换给程序增加了不必要的开销。相反,您可以像您所做的那样使用类型保证来避免这种开销。
此外,Abstract
甚至没有存在的理由。这是一个没有公开有用功能的接口。这里真正需要的是保存一个不相关接口的列表。
假设我们只键入 easure 以允许 copy_to_generic
将实例转换为父级 class。
template <typename Parent, typename T>
generic_t to_generic(T&& value) // forward is better.
{
struct local_cast
{
static void destroy(void* p)
{
// we cast to the parent first, and then to the real type.
delete static_cast<std::decay_t<T>*>(static_cast<Parent*>(p));
}
};
generic_t p;
p.obj = static_cast<Parent*>(new std::decay_t<T>(std::forward<T>(value)));
p.del = &local_cast::destroy;
return p;
}
看一下这段代码,类型很轻松:
// No useless interface
template<typename P>
struct AbstractHandler {
// No virtual destructor needed, generic_t already has a virtual destructor via `del`
virtual void handle(P) = 0;
};
template<typename P, typename H>
struct Handler : private H {
void handle(P p) override {
H::handle(p);
}
};
struct Test1 {};
struct Test1Handler {
void handle(Test1) {}
};
struct Test2 {};
struct Test2Handler {
void handle(Test2) {}
};
int main() {
std::vector<generic_t> handlers;
handlers.emplace_back(
to_generic<AbstractHandler<Test1>>(Handler<Test1, Test1Handler>{})
);
handlers.emplace_back(
to_generic<AbstractHandler<Test2>>(Handler<Test2, Test2Handler>{})
);
// some code later....
static_cast<AbstractHandler<Test1>*>(handlers[0].obj)->handle(Test1{});
static_cast<AbstractHandler<Test2>*>(handlers[1].obj)->handle(Test2{});
}
不再有空界面和动态转换!此代码与另一个代码执行相同的操作,但速度更快。
我正在读一本关于元编程的书,在 Trampolines 上有分裂:
struct generic_t
{
void* obj;
void(*del)(void*);
};
template <typename T> // outer template parameter
generic_t copy_to_generic(const T& value)
{
struct local_cast // local class
{
static void destroy(void* p) // void*-based interface
{
delete static_cast<T*>(p); // static type knowledge
}
};
generic_t p;
p.obj = new T(value); // information loss: copy T* to void*
p.del = &local_cast::destroy;
return p;
}
我完全明白它是如何工作的,但我不知道它的应用是什么!你通常在哪里使用这种技术?有人知道吗?谢谢:)
我在我的程序中的很多地方都用到了它。我喜欢这种方法的一件事是您可以保存一个不相关类型的列表。例如,我见过很多这样的代码:
struct Abstract { virtual ~Abstract() = default; };
template<typename P>
struct AbstractHandler : Abstract {
virtual void handle(P) = 0;
};
template<typename P, typename H>
struct Handler : AbstractHandler<P>, private H {
void handle(P p) override {
H::handle(p);
}
};
struct Test1 {};
struct Test1Handler {
void handle(Test1) {}
};
struct Test2 {};
struct Test2Handler {
void handle(Test2) {}
};
int main() {
std::vector<std::unique_ptr<Abstract>> handlers;
handlers.emplace_back(std::make_unique<Handler<Test1, Test1Handler>>());
handlers.emplace_back(std::make_unique<Handler<Test2, Test2Handler>>());
// some code later....
dynamic_cast<AbstractHandler<Test1>*>(handlers[0].get())->handle(Test1{});
dynamic_cast<AbstractHandler<Test2>*>(handlers[1].get())->handle(Test2{});
}
动态转换给程序增加了不必要的开销。相反,您可以像您所做的那样使用类型保证来避免这种开销。
此外,Abstract
甚至没有存在的理由。这是一个没有公开有用功能的接口。这里真正需要的是保存一个不相关接口的列表。
假设我们只键入 easure 以允许 copy_to_generic
将实例转换为父级 class。
template <typename Parent, typename T>
generic_t to_generic(T&& value) // forward is better.
{
struct local_cast
{
static void destroy(void* p)
{
// we cast to the parent first, and then to the real type.
delete static_cast<std::decay_t<T>*>(static_cast<Parent*>(p));
}
};
generic_t p;
p.obj = static_cast<Parent*>(new std::decay_t<T>(std::forward<T>(value)));
p.del = &local_cast::destroy;
return p;
}
看一下这段代码,类型很轻松:
// No useless interface
template<typename P>
struct AbstractHandler {
// No virtual destructor needed, generic_t already has a virtual destructor via `del`
virtual void handle(P) = 0;
};
template<typename P, typename H>
struct Handler : private H {
void handle(P p) override {
H::handle(p);
}
};
struct Test1 {};
struct Test1Handler {
void handle(Test1) {}
};
struct Test2 {};
struct Test2Handler {
void handle(Test2) {}
};
int main() {
std::vector<generic_t> handlers;
handlers.emplace_back(
to_generic<AbstractHandler<Test1>>(Handler<Test1, Test1Handler>{})
);
handlers.emplace_back(
to_generic<AbstractHandler<Test2>>(Handler<Test2, Test2Handler>{})
);
// some code later....
static_cast<AbstractHandler<Test1>*>(handlers[0].obj)->handle(Test1{});
static_cast<AbstractHandler<Test2>*>(handlers[1].obj)->handle(Test2{});
}
不再有空界面和动态转换!此代码与另一个代码执行相同的操作,但速度更快。