2017-02-09 12 views
0

Рассмотрим следующий пример, минимальный пример:Тип стиранием и вид шаблонный метод

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, что это не единственный доступный type), чтобы создать массив экземпляров 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; 
}; 

Это недалеко от того, что я сделал бы с помощью лямбда, но это более подробно и требует, чтобы я явно объявлял класс C.

Есть ли другая подходящая альтернатива для достижения того же или это единственное жизнеспособное решение?

+0

Я чувствую проблему [XY] (https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Можете ли вы дать нам код, который вы хотели бы написать, предполагая, что все поддерживающие магии шаблонов работают отлично? – nwp

+0

@ nwp Я дал нечто подобное. Представьте себе, что у меня был стертый массив типа (допустим, я), я бы хотел, чтобы он повторял его, вызывая 's.func (s.ptr, [] (auto obj) {/ * ... * /});', каждый время с немного отличающимися лямбдами. Могу я дать вам более подробную информацию? Я не знаю, как это объяснить. Прости. – skypjack

+0

Что общего между всеми элементами массива? У них просто есть общая функция-член 'void f()'? – nwp

ответ

0

Предполагая, что вы можете перечислить типы, которые вы хотите, чтобы поддержать это можно сделать:

#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 и итерации по PF с за один раз.