Jump to content

    
Sign in to follow this  
_pv

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

Recommended Posts

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

 

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

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> или рекурсию?

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

тогда до 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();

 

Share this post


Link to post
Share on other sites

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

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

Edited by rkit

Share this post


Link to post
Share on other sites
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), там всё просто.

Share this post


Link to post
Share on other sites
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

 

Share this post


Link to post
Share on other sites
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...>); }

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this