类型擦除和一种模板方法模式



考虑以下最小示例:

struct S {
using func_t = void(*)(void *);
template<typename T>
static void proto(void *ptr) {
static_cast<T*>(ptr)->f();
}
func_t func;
void *ptr;
};
struct T {
void f() {}
};
void g(S &s) {
s.func(s.ptr);
}
int main() {
T t;
S s;
s.func = &S::proto<T>;
s.ptr = &t;
g(s);
}

非常明显的想法是擦除一堆对象的类型(比如T,这不是唯一可用的类型),以创建S的实例数组,然后迭代该数组并调用一个预定的成员函数。

到目前为止还不错,它很容易实现,而且很有效
现在我想提供一个外部函数来调用已擦除的对象,如下所示:

template<typename T, typename F>
static void proto(void *ptr, F &&f) {
auto *t = static_cast<T*>(ptr);
std::forward<F>(f)(*t);
t->f();
}

或者这个:

template<typename T>
static void proto(void *ptr, void(*f)(T &)) {
auto *t = static_cast<T*>(ptr);
f(*t);
t->f();
}

调用为:

s.func(s.ptr, [](auto obj){ /* ... */ });

一种模板方法模式,其中额外的功能由调用者而不是派生类提供
不幸的是,我无法做到这一点,因为我无法将专业化简化为同构的东西来分配给函数指针。

我能看到的唯一替代方案是定义一个自定义类,如下所示:

struct C {
template<typename T>
void f(T &t) { /* ... */ }
// ...
};

如果f以某种方式在内部将调用分派给正确的成员函数,则将其用作:

struct S {
using func_t = void(*)(void *, C &);
template<typename T>
static void proto(void *ptr, C &c) {
auto t = static_cast<T*>(ptr);
c.f(*t);
t->f();
}
func_t func;
void *ptr;
};

这与我使用lambda所做的不太一样,但它更详细,需要我显式声明类C

是否有其他有效的替代方案来实现同样的目标,或者这是唯一可行的解决方案?

假设您可以枚举您希望支持的类型,则可以执行以下操作:

#include <iostream>
#include <string>
#include <vector>
template <class... Ts>
struct PseudoFunction {
private:
template <class T>
static void (*function)(T &);
template <class T>
static void call_func(void *object) {
return function<T>(*static_cast<T *>(object));
}
template <class Fun>
static void assign(Fun) {}
template <class Fun, class Head, class... Tail>
static void assign(Fun fun) {
function<Head> = fun;
assign<Fun, Tail...>(fun);
}
public:
template <class T>
PseudoFunction(T *t)
: object(t)
, func(call_func<T>) {}
template <class F>
static void set_function(F f) {
assign<F, Ts...>(f);
}
void operator()() {
func(object);
}
private:
void *object;
void (*func)(void *);
};
template <class... Ts>
template <class T>
void (*PseudoFunction<Ts...>::function)(T &) = nullptr;
//example types that are not related and not copy constructible
//but have the same member function name and signature
struct T1 {
T1() = default;
T1(const T1 &) = delete;
void func(double d) {
std::cout << "T1: " + std::to_string(d) + 'n';
}
};
struct T2 {
T2() = default;
T2(const T2 &) = delete;
void func(double d) {
std::cout << "T2: " + std::to_string(d) + 'n';
}
};
int main() {
T1 t1;
T2 t2;
using PF = PseudoFunction<T1, T2>;
std::vector<PF> funcs;
funcs.push_back(&t1);
funcs.push_back(&t2);
PF::set_function([](auto &object) { object.func(3.14); });
for (auto &f : funcs) {
f();
}
}

(演示)

它具有良好的调用语法(只是在调用对象之前必须指定函数)和设置可能未使用的函数指针的一些开销。

可以制作一个包装器,一次性完成set_functionPF

相关内容

最新更新