Я пытаюсь написать оболочку c ++ 11 вокруг C API, и в основном есть способ регистрации уведомлений с помощью указателя функции stati c, который также возвращает мне «непрозрачный» указатель , которые предоставляются позже, в основном указатель на классы, которые я создаю, в этом примере class foo
. По сути, я пытаюсь создать функцию-помощник stati c `` помощник <..> :: call, которая имеет сигнатуру API, но генерирует код для вызова моей функции-члена в экземпляре, созданном оболочкой c ++, и передается в через "непрозрачный" указатель вместе с ним. Эта функция stati c затем также преобразует аргументы при окончательном вызове функции-члена.
Мне кажется, что это почти работает, но у меня возникают проблемы с созданием более "хороших" пабликов. c функция register_handler
в этом примере, которая скрывает "уродливые" внутренние органы. Это ошибка, которую я получаю:
test.cpp:154:37: error: no matching function for call to ‘register_handler<&foo::bar>(const char [6])’
154 | register_handler<&foo::bar>("test2"); // <-- trying to wrap it into a function so I can use only one template argument
| ^
test.cpp:137:6: note: candidate: ‘template<class T, class R, class ... Args, R (T::* Func)(Args ...)> void register_handler(const char*)’
137 | void register_handler(const char* name)
| ^~~~~~~~~~~~~~~~
Это мой тестовый код:
#include <iostream>
#include <memory>
#include <vector>
#include <map>
#include <cassert>
// inspired by https://stackoverflow.com/a/7943765/2129246
template <typename T>
struct func_traits:
public func_traits<decltype(&T::operator())>
{
};
template <typename R, typename... Args>
struct func_traits<R(*)(Args...)>
{
enum { arity = sizeof...(Args) };
typedef R result_type;
using all_args = std::tuple<Args...>;
template <size_t i>
struct arg
{
typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
};
};
template <typename C, typename R, typename... Args>
struct func_traits<R(C::*)(Args...) const>
{
enum { arity = sizeof...(Args) };
typedef C class_type;
typedef R result_type;
using all_args = std::tuple<Args...>;
template <size_t i>
struct arg
{
typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
};
};
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;
};
struct value
{
std::string str_;
template <typename T>
value(T val):
str_(std::to_string(val))
{
}
value(const char* str):
str_(str)
{
}
value(const std::string& str):
str_(str)
{
}
operator int() const
{
return std::stoi(str_);
}
operator double() const
{
return std::stof(str_);
}
operator std::string() const
{
return str_;
}
};
std::map<std::string, void(*)(void*, const std::vector<value>&)> g_handlers;
template <typename T, T>
struct helper;
template <typename T, typename R, typename... Args, R(T::*Func)(Args...)>
struct helper<R(T::*)(Args...), Func>
{
template <size_t... Is>
static void expand(T* obj, const std::vector<value>& args, indices<Is...>)
{
assert(sizeof...(Is) <= args.size());
(obj->*Func)((args[Is])...);
}
static void call(void *p, const std::vector<value>& args)
{
T* obj = reinterpret_cast<T*>(p);
expand(obj, args, typename make_indices<sizeof...(Args)>::type());
}
static void reg_handler(const char* name)
{
g_handlers.insert(std::make_pair(name, call));
};
};
template <typename Obj>
void call_handler(Obj& obj, const char* name, const std::vector<value>& args)
{
auto it = g_handlers.find(name);
if (it != g_handlers.end())
it->second(reinterpret_cast<void*>(&obj), args);
else
std::cout << "handler not registered: " << name << std::endl;
}
// The code below somehow doesn't ever match this template
template <typename T, typename R, typename... Args, R(T::*Func)(Args...)>
void register_handler(const char* name)
{
helper<R(T::*)(Args...), Func>::reg_handler(name);
}
struct foo
{
void bar(int v, const std::string& str, double f)
{
std::cout << "bar: v=" << v << " str=" << str << " f=" << f << std::endl;
};
};
int main()
{
// register member function handlers before we have any instances
helper<decltype(&foo::bar), &foo::bar>::reg_handler("test"); // <-- works, but "ugly" and exposes internal implementation
register_handler<&foo::bar>("test2"); // <-- trying to wrap it into a function so I can use only one template argument
// now we have an instance
foo f;
// call the previously registered handler
call_handler(f, "test", {1, "2", 3.45});
call_handler(f, "test2", {1, "2", 3.45});
return 0;
}