Перейти к содержанию
    

с++, шаблоны, указатели на функции

решил тут посмотреть что там нынче современные плюсы умеют

 

вот есть обычные С функции, например

int f1(double a); void f2 (double b, const char * с); ...

хочется сделать для них некую простую обёртку, без лишней писанины,

lua_push(L, f1);

которая во время компиляции сгенерит шаблонную функцию в зависимости от типа аргументов, которая уже достанет соответственно типам аргументы, позовёт саму оборачиваемую функцию и получит возвращаемое значение.

lua тут просто для примера.

 

обёрнуто ещё раз в шаблонную функцию, а не в стуктуру, потому что там какие-то грабли были с перегрузкой конструктора если вдруг не только указатели на функции передавать.
 

template <typename T, typename ...Args> void lua_push(lua_State * L, T(*value)(Args...)){
  push<0, T, Args...>(L, value);
};

template <> void lua_push(lua_State * L, int value){lua_pushinteger(L, value);}
template <> void lua_push(lua_State * L, double value){lua_pushnumber(L, value);}
template <> void lua_push(lua_State * L, const char * value){lua_pushstring(L, value);}
template <> void lua_push(lua_State * L, int(*value)(lua_State*)){lua_pushcfunction(L, value);}

 

template <int N, typename T, typename ...Args> struct push{
  push(lua_State * L, T(* func)(Args...)){
    push::m_func = func;
    lua_push(L, push::cfunc);
  }
  static T(*m_func)(Args...);
  static int cfunc (lua_State * L){
    lua_push(L, m_func((lua_check<Args>(L, 1))...));
    return 1;
  }
};

 

в луа отдаётся статическая функция-обёртка push::cfunc и соответственно сам указатель на функцию сохраняется в статический же push::m_func.

template <int N, typename T, typename ...Args> T(*push<N, T,Args...>::m_func)(Args...);

это не совсем работает из-за того что разные функции с одинаковыми типами возвращаемых значений и аргументов перезапишут общий для них m_func.

сам указатель из агрумента функции вытащить в константный параметр шаблона не получилось, не смотря на то что 

constexpr auto func = &printf; вроде вполне законно, но при проходе через "аргумент" функции он константным быть перестаёт.

 

а при указании в качестве параметра шаблона структуры

template <typename T, typename ...Args, T(*Fp)(Args...)>

в конец его поставить нельзя потому что вариативные Args... должны быть в конце, а до Args... нельзя потому что Args ещё не определён.

причём для шаблонной функции позволяет сделать template <typename T, typename ...Args, T(*Fp)(Args...)>, но как вытащить сам указатель из аргумента функции lua_push?

 

а при попытке всё же протащить адрес функции как int N в качестве первого параметра шаблона через всевозможные касты удалось даже поломать gcc:

internal compiler error: in coerce_template_parms, at cp/pt.c:8519|

 

может как-то по другому можно заставить всегда генерить разные шаблонные типы даже для одинаковых параметров шаблона?

какой-нибудь compile time счётчик в качестве <int N> или рекурсию?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Может быть не объявлять m_func статической, и она будет разной для разных функций? В любом случае, мало чего понятно. Людей, которые мастера шаблонов, и при этом знают, что такое lua_push, не так уж и много.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

тогда до m_func не добраться из статической функции cfunc и надо где-то хранить и как-то передавать сам объект.

пусть будет без lua, она тут просто для примера.

 

хотелось для обычной функции сгенерить другую шаблонную функцию-обёртку, которая достанет откуда надо аргументы исходя из типов аргументов, позовёт исходную функцию с данными аргументами и положит куда надо результат. средствами компилятора, а не сторонними кодогенераторами или в рантайме.

псевдокод:

template <typename T, typename Args...> std::function<> wrap( T (*func)(Args...) ){
  return [](){
    Args ... args = get_agrs()...;
    T ret = func(args...);
    do_something(ret);
  }
}


f1 = wrap(sqrt);  //double(*)(double)
f2 = wrap(pow);  //double(*)(double, double)
f3 = wrap(getch);  //char(*)(void)


f1();
f2();

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну нужно сделать просто шаблон-структуру, для которой определен operator(), это будет твоя f1. В ней же хранить и m_func и cfunc.

И посмотреть на std::apply.  Самое интересное остается в реализации get_args().

Изменено пользователем rkit

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

17 hours ago, _pv said:

может как-то по другому можно заставить всегда генерить разные шаблонные типы даже для одинаковых параметров шаблона?

Ну, допустим вы это даже как-то сгенерируете. Выбирать нужный вам вариант компилятор как будет?

Вас точно не устраивает простейший вариант?

void push(int i) { pushinteger(i); }
void push(float f) { pushnumber(f); }

Ну или полистайте гитхаб в поисках плюсовых обёрток для Луа. Там оно есть.

 

 

5 hours ago, rkit said:

что такое lua_push

Это десяток однотипных функций, чтобы поместить аргумент в стек перед вызовом функции в интерпретаторе луа (по отдельной функции для каждого типа аргумента). Можете посмотреть описание (https://www.lua.org/manual/5.3/manual.html раздел 4), там всё просто.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 minutes ago, esaulenka said:

Ну, допустим вы это даже как-то сгенерируете. Выбирать нужный вам вариант компилятор как будет?

Вас точно не устраивает простейший вариант?


void push(int i) { pushinteger(i); }
void push(float f) { pushnumber(f); }

Ну или полистайте гитхаб в поисках плюсовых обёрток для Луа. Там оно есть.

выбирать будет исходя из типа переданного значения.


простейший вариант не устраивает если я захочу передать функцию 

void push(pow); 

для этого руками придётся сделать:

int lua_pow(lua_State *L){
  double arg0 = lua_tonumber(L,1);
  double arg1 = lua_tonumber(L,2);
  double ret = pow(arg0,arg1);
  lua_pushnumber(L, ret);
  return 1;
}

void push(pow);  {pushcfunction (L, lua_pow);}

при том что всё это компилятор мог бы сгенерить и сам по шаблону исходя из типа переданного ему pow: double(*)(double, double).

 

и ещё раз, не в луа дело, я разобраться с шаблонами хочу.

готовых обёрток действительно полно http://lua-users.org/wiki/BindingCodeToLua , последняя ссылка там более менее описывает то, чего хочется http://inspired-code.blogspot.com/p/automagical-language-bindings-using-c.html

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

23 hours ago, rkit said:

 Самое интересное остается в реализации get_args().

parameter pack умеет сам разворачиваться f(args)...  <==>   f(arg0), f(arg1), ...

 

вот что пока получилось, единственное что не смог победить, это то что саму функцию приходится указывать два раза, один раз как параметр шаблона, но как void*,  а другой раз как аргумент для получения типа.

lua_push<(void*)func>(L, func);

 

 
template <typename T> void lua_push(lua_State * L, T value);
template <> void lua_push (lua_State * L, int value) { lua_pushinteger(L, value); }
template <> void lua_push (lua_State * L, double value) { lua_pushnumber(L, value); }
template <> void lua_push (lua_State * L, const char * value) { lua_pushstring(L, value); }
template <> void lua_push (lua_State * L, int(*func)(lua_State *)) { lua_pushcfunction(L, func); }

template <typename T> T lua_to(lua_State * L, int idx);
template <> int lua_to (lua_State * L, int idx)      { return lua_tointeger (L, idx); }
template <> double lua_to (lua_State * L, int idx)      { return lua_tonumber (L, idx); }
template <> const char * lua_to (lua_State * L, int idx)      { return lua_tostring (L, idx); }

template<int ...> struct seq { };
template<int N, int ...S> struct gens : gens<N-1, N-1, S...> { };
template<int ...S> struct gens<0, S...> { typedef seq<S...> type; };
template <void* Fn, typename T, typename ... Args, int ... N> T callN(lua_State *L, int * retnum, seq<N...>){ return ((T(*)(Args...))Fn)(lua_to<Args>(L, N+1)...); }
template <void* Fn, typename T, typename ... Args> T call(lua_State * L, int * retnum){ return callN<Fn,T,Args...>(L, retnum, typename gens<sizeof...(Args)>::type ()); }

template <void* Fn, typename T, typename ... Args> int lua_cfunc(lua_State * L){
  int retnum = 0;
  if constexpr (std::is_void<T>::value) 
    call<Fn,T,Args...>(L, &retnum);
  else {
    lua_push(L, call<Fn,T,Args...>(L, &retnum));
    retnum += 1;
  }
  return retnum;
}

template <void* F, typename T, typename ... Args> void lua_push(lua_State * L, T(*)(Args...)){ lua_push(L, lua_cfunc<(void*)F, T, Args...>); }

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...