【问题标题】:Type erasure and a kind of template method pattern类型擦除和一种模板方法模式
【发布时间】:2017-02-09 23:33:09
【问题描述】:

考虑以下最小示例:

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

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

【问题讨论】:

  • 我感觉到XY problem。假设您拥有所有支持模板的魔力完美运行,您能否给我们您想要编写的代码?
  • @nwp 我给了类似的东西。想象一下,有(让我说)类型的擦除数组,我想迭代它调用s.func(s.ptr, [](auto obj){ /* ... */ });,每次使用略有不同的 lambda。我可以给你更多的细节吗?我不知道如何解释它更好。对不起。
  • 数组中所有元素的共同点是什么?他们只有一个共同的成员函数void f()吗?
  • @nwp 它们是完全不相关的类型,没有共同的基础,它们提供了一组有限的成员函数,具有相同的名称并接受相同的参数。
  • 是什么阻止您使用std::vector&lt;std::function&lt;Retval_t(Param_t)&gt;&gt; 并为各种类型的object 填充[object](Param_t param){ return object.func(param); };?可以编写一个模板,给定一个异构对象列表将返回这样一个向量。

标签: c++ templates software-design type-erasure


【解决方案1】:

假设您可以枚举您希望支持的类型,您可以这样做:

#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();
    }
}

(demo)

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

也许可以制作一个包装器来执行 set_function 并一次性遍历 PFs。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-07
    • 2017-11-23
    • 1970-01-01
    • 2013-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多