Предаване на позиция на аргумент на променлив шаблон

Бих искал да създам функция, която приема променлив брой аргументи на шаблона. По-късно с тези аргументи функцията трябва да предаде своята позиция по следния начин:

template<typename R, typename Args...>
R myFunction(Data &data, void *function) {
    auto f = (R (*)(Args...))function;
    return f(read<Args1>(data, 1), read<Args2>(data, 2), ...);// <-- This is the problem
}

Даденият код разбира се не може да се компилира. Има ли начин да го оправя? Има ли начин да го направите без различни шаблони без твърде много дублиране на код?


person Juraj Blaho    schedule 04.03.2013    source източник
comment
Сигурен съм, че можете да избегнете прехвърлянето на указател на обект към указател на функция.   -  person sellibitze    schedule 04.03.2013
comment
@sellibitze: Това проблем ли е? Тъй като мога да имам само void* указател потребителски данни, които се предават от C функция.   -  person Juraj Blaho    schedule 04.03.2013
comment
Данните могат да бъдат void *. Това, че функцията е void *, е проблем.   -  person Vaughn Cato    schedule 04.03.2013
comment
@JurajBlaho: Този вид отливки е изключително непреносим. Предлагам ви да обясните какъв проблем възнамерявате да разрешите по този начин. Вероятно ще получите по-полезни отговори, ако го направите. Струва ми се, че имаме малко X/Y проблем.   -  person sellibitze    schedule 04.03.2013
comment
@sellibitze: Благодаря за коментара, сега виждам, че указателят на функцията за кастинг към void* е UB. Ще трябва да го реша малко по-различно, но все пак мога да разреша проблема, като използвам идеи от приетия отговор.   -  person Juraj Blaho    schedule 05.03.2013
comment
Вместо да използвам void* за аргумента function, щях да добавя друг параметър на шаблона за типа на функцията, тогава не е необходимо преобразуването на типа.   -  person Some programmer dude    schedule 05.03.2013
comment
@JoachimPileborg: Самата тази функция е лесна за правилно изпълнение. Но C API, който трябва да използвам, може да съхранява само void* данни. Ще трябва да обвия указателя на функцията в struct.   -  person Juraj Blaho    schedule 05.03.2013


Отговори (1)


Да, това е възможно:

// we need a compile-time helper to generate indices
template< std::size_t... Ns >
struct indices
{
  typedef indices< Ns..., sizeof...( Ns ) > next;
};

template< std::size_t N >
struct make_indices
{
  typedef typename make_indices< N - 1 >::type::next type;
};

template<>
struct make_indices< 0 >
{
  typedef indices<> type;
};

С тези помощници се нуждаете от един препращач за вашата функция като тази:

template<typename R, typename... Args, std::size_t... Ns>
R myFunctionImpl(void *Data, void *function, indices<Ns...> ) {
    auto f = (R (*)(Args...))function;
    return f(read<Args>(Data, Ns + 1)...);// +1 because indices is zero-based
}

template<typename R, typename... Args>
R myFunction(void *Data, void *function) {
   return myFunctionImpl< R, Args... >( Data, function, typename make_indices<sizeof...(Args)>::type() );
}

РЕДАКТИРАНЕ: Как работи? Първо определяме размера на пакета аргументи Args чрез sizeof.... make_indices<N>::type след това се разширява в indices<0,1,2,...,N-1>. Той се дава като допълнителен параметър към функцията за внедряване (от пренасочващия, който просто създава фиктивен екземпляр), следователно дедукцията на аргументи се включва от страна на функцията за изпълнение и поставя генерираните индекси в пакета аргументи Ns.

Функцията за изпълнение вече има два пакета аргументи с еднакъв размер, а именно Args и Ns. Когато се разшири чрез многоточие ..., многоточието разширява целия израз, към който е приложено, и разширява паралелно всички пакети с параметри! В горния пример този израз е read<Args>(Data, Ns+1), който добре се разширява в псевдокода на OP.

person Daniel Frey    schedule 04.03.2013
comment
можеш ли да публикуваш прост пример? - person BЈовић; 04.03.2013
comment
Току що го направих. Какво липсва в примера, който дадох? - person Daniel Frey; 04.03.2013
comment
Това, което липсва, е реален пример. С main() и някои функции (с 1,2,3 параметъра). Как използвате тези функции и структури? - person BЈовић; 05.03.2013
comment
Отне ми известно време, за да разбера напълно кода и изглежда, че трябва да работи. - person Juraj Blaho; 05.03.2013
comment
Все още не разбирам. Как се компилира това: typename make_indices<sizeof...(Args)>::type()? Трябва да е грешка при компилиране. - person BЈовић; 05.03.2013
comment
Това е добре позната техника и се компилира. Добавих малко пояснение към отговора си. - person Daniel Frey; 05.03.2013