Related
This question already has answers here:
split variadic template arguments
(5 answers)
Closed 2 years ago.
I wrote a convenience function that gets some arguments and then sends them
to the right functions:
void combine(int a, int b, double one, double two) {
funA(a,b);
funB(one, two);
}
Now I want to do it again, but with variadic template parameters for both my combine function and the funA-funB pair:
//somewhere earlier in the code
template<class U, class T> funA(U par1, T par2) {...}
template<class X, class Y> funB(X par1, Y par2) {...}
template<class ...ParamA, class ...ParamB>
void combine(ParamA...packA, ParamB...packB) {
funA(packA...);
funB(packB...);
}
This of course can't work because I need some way to divide the parameters list in half.
But what is more interesting is when I try to compile the above code with a call like
combine(10, 'a', 12.0, true) I get this error:
In instantiation of ‘void combine(ParamA ..., ParamB ...) [with ParamA = {}; ParamB = {int, char, double, bool}]’:
...
error: no matching function for call to ‘funA()’
....
error: no matching function for call to ‘funB(int&, char&, double&, bool&)’
which shows that ParamB "ate" all of the parameter list.
So, my question is: is there a way to divide the parameter list of a function with variable template list in half?. If not, how else may I write my combine function?
#include <tuple>
#include <utility>
#include <iostream>
#include <memory>
//somewhere earlier in the code
template<class U, class T> void funA(U par1, T par2) {
std::cout<<par1 <<' '<< par2 <<'\n';
}
// Use move-only type to check forwarding
template<class X> void funB(X par1, std::unique_ptr<int> par2) {
std::cout<<par1 <<' '<< *par2 <<'\n';
}
// Call fnc with a subset of arguments, more general than we need for the partitioning.
template<typename Fnc,std::size_t...Is, typename...Args>
auto call_sub(Fnc fnc, std::index_sequence<Is...>, Args&&...args){
auto tup = std::forward_as_tuple(std::forward<Args>(args)...);
// Extract relevant indices.
return fnc(std::get<Is>(std::move(tup))...);
}
// Shift index sequences
template< std::size_t shift_amount,std::size_t...Is>
constexpr auto shift_sequence( std::index_sequence<Is...>){
return std::index_sequence<shift_amount +Is...>{};
}
template<class ...Params>
void combine(Params&&...pack) {
static_assert(sizeof...(pack) % 2 == 0);
constexpr std::size_t half = sizeof...(pack) /2;
constexpr std::make_index_sequence<half> first_half;
constexpr auto second_half = shift_sequence<half>(first_half);
// Lambda must be used because funA nor funB are not functions, they are templates which cannot be passed around.
call_sub([](auto&&...args){return funA(std::forward<decltype(args)>(args)...);},
first_half, std::forward<Params>(pack)...);
call_sub([](auto&&...args){return funB(std::forward<decltype(args)>(args)...);},
second_half, std::forward<Params>(pack)...);
}
int main (int argc, char *argv[])
{
combine(1,2,3,std::make_unique<int>(10));
}
Output:
1 2
3 10
Live Godbolt demo
Usually when you write a CLI tool which accepts parameter you have to deal with them. Most of the time you want to switch between behaviours based on the value of an argument.
The following is a common use case, where the program accepts a type and then prints something based on that type. I am using Boost to pre-process and auto generate the whole if-else branches.
This is very nice in terms of maintainability as I only need to update a define when I introduce a new type. On the other hand it is quite far from being modern and elegant.
I thought about using better-enums to avoid using the if-else to convert from string into an enum using the _from_string utility function. But then the way to go from enum to a type is still obscure to me.
Any suggestion on how to keep the nice maintainability of the current implementation but avoid to use pre-processor and macro functionalities?
#include <iostream>
#include <cstdlib>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <type_traits>
using a_type = int;
using b_type = long;
using c_type = float;
using d_type = double;
#define TYPES (a)(b)(c)(d)
template<typename T>
void foo(){
T num = 1;
std::cout << typeid(decltype(num)).name() << " : "<< num << std::endl;
};
int main(int argc, char **argv)
{
if (argc < 1) {
return 1;
}
std::string type = argv[1];
if (false) {
#define LOOP_BODY(R, DATA, T) \
} \
else if (type == BOOST_PP_STRINGIZE(T)) { \
foo<BOOST_PP_CAT(T, _type)>(); \
BOOST_PP_SEQ_FOR_EACH(LOOP_BODY, _, TYPES);
#undef LOOP_BODY
} else {
std::cout << "ERROR: Unknown type " << type << std::endl;
}
}
Working example at https://wandbox.org/permlink/60bAwoqYxzU1EUdw
Another way is to use a plain array and std::find_if instead of if-else:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
#include <typeinfo>
struct Handler {
char const* name;
void(*fn)(std::string const&); // Or std::function<> to accept lambdas.
};
struct A {};
struct B {};
template<class T>
void foo(std::string const& name) {
std::cout << "foo<" << typeid(T).name() << ">: " << name << '\n';
}
int main(int, char** av) {
Handler const handlers[] = {
{"a", foo<A>}
, {"b", foo<B>}
};
std::string const name = av[1];
auto handler = std::find_if(std::begin(handlers), std::end(handlers), [&name](auto const& h) {
return name == h.name;
});
if(handler != std::end(handlers))
handler->fn(name);
}
You don't need to use the preprocessor to store an arbitrary list of types and generate code for them. We can use variadic templates and compile-time strings. You can isolate preprocessor usage to the generation of pairs of names and types.
Firstly, let's define a wrapper for a compile-time sequence of characters. Note that the use of the _cs literal is non-Standard, but available in every major compiler and likely to be part of C++20:
template <char... Cs>
using ct_str = std::integer_sequence<char, Cs...>;
template <typename T, T... Cs>
constexpr ct_str<Cs...> operator""_cs() { return {}; }
We can then define an empty type that stores a pair of a name and a type:
template <typename Name, typename T>
struct named_type
{
using name = Name;
using type = T;
};
And a macro to conveniently instantiate it:
#define NAMED_TYPE(type) \
named_type<decltype(#type ## _cs), type>
You can now use an empty variadic template class to store your types:
template <typename... Ts>
struct named_type_list { };
using my_types = named_type_list<
NAMED_TYPE(int),
NAMED_TYPE(long),
NAMED_TYPE(float),
NAMED_TYPE(double)
>;
Now, let's see how our main should look:
int main()
{
const std::string input{"float"};
handle(my_types{}, input, [](auto t)
{
print(typename decltype(t)::name{});
});
}
The above will print out "float". We can implement handle by unpacking the list of named_type types and using a fold expression to find the matching type name:
template <typename... Ts, typename F>
void handle(named_type_list<Ts...>, const std::string& input, F&& f)
{
( (same(input, typename Ts::name{}) && (f(Ts{}), true) ) || ...);
}
Checking for equality between std::string and ct_str is annoying, but doable:
template <std::size_t... Is, char... Cs>
bool same_impl(const std::string& s,
std::integer_sequence<char, Cs...>,
std::index_sequence<Is...>)
{
return ((s[Is] == Cs) && ...);
}
template <char... Cs>
bool same(const std::string& s, std::integer_sequence<char, Cs...> seq)
{
return s.size() >= sizeof...(Cs)
&& same_impl(s, seq, std::make_index_sequence<sizeof...(Cs)>{});
}
final result live on wandbox.org
Note that this answer uses C++17 fold expressions. You can replace them in C++14 with one of the following techniques:
Recursive variadic template function, where the base case returns the default accumulation value, and the recursive case performs an operation between the tail and the head.
C++11 pack expansion tricks such as for_each_argument.
The dispatching does short-circuit:
( (same(input, typename Ts::name{}) && (f(Ts{}), true) ) || ...);
This fold expression will stop at the first invocation of f thanks to the , true expression and the || operator.
empirical proof on wandbox.org
EDIT: Problem solved, it was just wrong function declaration order.
when trying to solve a problem which seemed to be quite trivial at first glance, I stumbled over behaviour I can not explain.
I want to process an arbitrary int-array recursively and of course I have to stop the recursion somehow.
As partial specialisation to concrete numbers is not possible with template functions (like
template<typename T, int N> foo<T, 0>(void)
), I tried to fake this with SFINAE. But when I want to call the second SFNIAE-function from the first one, I get a compiler error.
Complete code example:
#include <algorithm>
#include <iostream>
using namespace std;
// -------------------------------------------------------------
template<typename T, int N>
void foo(T const& param, typename enable_if<N != 0, int>::type* = 0)
{
cout << "recursive step " << N << endl;
/* --- This was, what I desired: --- */
//foo<T, N - 1>(param);
/* --- THIS IS CAUSING AN ERROR! --- */
foo<T, 0>(param);
}
// -------------------------------------------------------------
template<typename T, int N>
void foo(T const& param, typename enable_if<N == 0, int>::type* = 0)
{
cout << "finish recursion" << endl;
}
// =============================================================
int main()
{
int a[5] = {0, 1, 2, 3, 4};
foo<decltype(a), 5>(a);
/* --- SAME CALL AS WITHIN foo(), BUT CAUSING NO ERROR! --- */
foo<decltype(a), 0>(a);
}
The compiler tells me:
main.cpp:9: Fehler: no type named 'type' in 'struct std::enable_if'
So it seems he somehow cannot solve for the second function.
However, if I call the function from main(), its not a problem.
Its my first time with SFINAE, I hope I made no trivial mistakes.
Thanks to everyone who read this far!
I'm pleasantly surprised with Clang's helpfulness on this one:
main.cpp:14:5: error: call to function 'foo' that is neither visible in the template definition nor found by argument-dependent lookup
foo(param);
^
main.cpp:29:5: note: in instantiation of function template specialization 'foo' requested here
foo(a);
^
main.cpp:19:6: note: 'foo' should be declared prior to the call site
void foo(T const&, typename std::enable_if::type* = 0)
^
I mean, it's just one step away from logging in on Stack Overflow and giving the answer itself. Anyway.
Add the declaration of the second function above the first one, so you can call it from therein:
template<typename T, int N>
void foo(T const&, typename std::enable_if<N == 0, int>::type* = 0);
Note that you must remove the default argument from the definition as well.
You need to switch the order of the definition (or declare the trivial case before the normal case), the normal case does not see the trivial one so it cannot call it:
template<typename T, int N>
void foo(T const& param, typename enable_if<N == 0, int>::type* = 0) {
cout << "finish recursion" << endl;
}
template<typename T, int N>
void foo(T const& param, typename enable_if<N != 0, int>::type* = 0) {
cout << "recursive step " << N << endl;
foo<T, N - 1>(param);
}
Note that you could use a helper class here to avoid this enable_if (and also change the signature so that you could let the compiler deduce parameters):
template<typename T, int N>
struct foo_h {
static void call(T const& param) {
foo_h<T, N - 1>::call(param);
}
};
template<typename T>
struct foo_h<T, 0> {
static void call(T const& param) {
}
};
template<typename T, int N>
void foo(const T (¶m)[N]) {
foo_h<const T[N], N>::call(param);
}
Then:
int arr[] = {1, 2, 3, 4, 5};
foo(arr); // Automatic template parameters deduction!
I am experimenting with some tuples, and I find myself in the weird position of asking this: how can I copy two tuples that differ in their sizes? Of course, this is intended limited to the minimum length of the two tuples.
So, for instance, let's create three tuples:
std::tuple<int, char, float> a(-1, 'A', 3.14);
std::tuple<int, char, double> b = a;
std::tuple<long, int, double, char> c;
Now, a and b differ in types, and the assignment work (obviously). As for a and c the things get a little more confusing.
My first implementation failed, since I don't know how to recurse on variadic templates with a specific type, so something like this won't work:
template <class T, class U>
void cp(std::tuple<T> from, std::tuple<U> to)
{
}
template <class T, class... ArgsFrom, class U, class... ArgsTo>
void cp(std::tuple<T, ArgsFrom...> from, std::tuple<U, ArgsTo...> to)
{
std::get<0>(to) = std::get<0>(from);
// And how to generate the rest of the tuples?
}
That function won't do anything. So I've devised a second failing attempt, using not the types, but the sizes:
template<class From, class To, std::size_t i>
void copy_tuple_implementation(From &from, To &to)
{
std::get<i>(to) = std::get<i>(from);
copy_tuple_implementation<From, To, i - 1>(from, to);
}
template<>
void copy_tuple_implementation<class From, class To, 0>(From &from, To &to)
{
}
template<class From, class To>
void copy_tuple(From &from, To &to)
{
constexpr std::size_t from_len = std::tuple_size<From>::value;
constexpr std::size_t to_len = std::tuple_size<To>::value;
copy_tuple_implementation<From, To, from_len < to_len ? from_len - 1 : to_len - 1>(from, to);
}
But that won't compile. I have too many errors to display here, but the most significant ones are:
Static_assert failed "tuple_element index out of range"
No type named 'type' in 'std::__1::tuple_element<18446744073709551612, std::__1::__tuple_types<> >'
Read-only variable is not assignable
No viable conversion from 'const base' (aka 'const __tuple_impl<typename __make_tuple_indices<sizeof...(_Tp)>::type, int, int, double>') to 'const __tuple_leaf<18446744073709551615UL, type>'
The interesting part is the index out of range, and the fact that I cannot copy an element with std::get<>.
Can anyone help me in this?
Thanks!
Here's one possibility, using C++14's ready-made integer sequence template (but this is easily reproduced manually if your library doesn't include it):
#include <tuple>
#include <utility>
template <std::size_t ...I, typename T1, typename T2>
void copy_tuple_impl(T1 const & from, T2 & to, std::index_sequence<I...>)
{
int dummy[] = { (std::get<I>(to) = std::get<I>(from), 0)... };
static_cast<void>(dummy);
}
template <typename T1, typename T2>
void copy_tuple(T1 const & from, T2 & to)
{
copy_tuple_impl(
from, to,
std::make_index_sequence<std::tuple_size<T1>::value>());
}
Example:
#include <iostream>
int main()
{
std::tuple<int, char> from { 1, 'x' };
std::tuple<int, char, bool> to;
copy_tuple(from, to);
std::cout << "to<0> = " << std::get<0>(to) << "\n";
}
Another option is to use operator overloading to simulate partial-specialization of your function:
template <std::size_t N>
struct size_t_t {};
template<class From, class To, std::size_t i>
void copy_tuple_implementation(From &from, To &to, size_t_t<i>)
{
std::get<i>(to) = std::get<i>(from);
copy_tuple_implementation(from, to, size_t_t<i-1>{});
}
template<class From, class To>
void copy_tuple_implementation(From &from, To &to, size_t_t<0>)
{
std::get<0>(to) = std::get<0>(from);
}
Or you could just use a helper class:
template<class From, class To, std::size_t i>
struct CopyTuple
{
static void run(From &from, To &to)
{
std::get<i>(to) = std::get<i>(from);
CopyTuple<From,To,i-1>::run(from, to);
}
};
template<class From, class To>
struct CopyTuple<From,To,0>
{
static void run(From &from, To &to)
{
std::get<0>(to) = std::get<0>(from);
}
};
The goal here is to get a clean syntax at point of use.
I define auto_slice which takes a tuple, and auto slices it for the expression.
The intended use is
auto_slice(lhs)=auto_slice(rhs);
and it just works.
// a helper that is a slightly more conservative `std::decay_t`:
template<class T>
using cleanup_t = std::remove_cv_t< std::remove_reference_t< T > >;
// the workhorse. It holds a tuple and in an rvalue context
// allows partial assignment from and to:
template<class T,size_t s0=std::tuple_size<cleanup_t<T>>{}>
struct tuple_slicer{
T&&t;
// Instead of working directly within operators, the operators
// call .get() and .assign() to do their work:
template<class Dest,size_t s1=std::tuple_size<Dest>{}>
Dest get() && {
// get a pack of indexes, and use it:
using indexes=std::make_index_sequence<(s0<s1)?s0:s1>;
return std::move(*this).template get<Dest>(indexes{});
}
template<class Dest,size_t s1=std::tuple_size<Dest>{},size_t...is>
Dest get(std::index_sequence<is...>) && {
// We cannot construct a larger tuple from a smaller one
// as we do not know what to populate the remainder with.
// We could default construct them, I guess?
static_assert(s0>=s1,"use auto_slice on target");
using std::get;
return Dest{ get<is>(std::forward<T>(t))... };
}
// allows implicit conversion from the slicer:
template<class Dest>
operator Dest()&&{
return std::move(*this).template get<Dest>();
}
// now we are doing the assignment work. This function
// does the pack expansion hack, excuse the strangeness of the
// code in it:
template<class Src, size_t...is>
void assign(std::index_sequence<is...>,tuple_slicer<Src>&&rhs)&&{
using std::get;
int _[]={0,(void(
get<is>(std::forward<T>(t))=get<is>(std::forward<Src>(rhs.t))
),0)...};
(void)_; // remove warnings
}
// assign from another slicer:
template<class Src,size_t s1>
void operator=(tuple_slicer<Src,s1>&&rhs)&&{
using indexes=std::make_index_sequence<(s0<s1)?s0:s1>;
std::move(*this).assign(indexes{},std::move(rhs));
}
// assign from a tuple. Here we pack it up in a slicer, and use the above:
template<class Src>
void operator=(Src&& src)&&{
std::move(*this) = tuple_slicer<Src>{ std::forward<Src>(src) };
}
};
// this deduces the type of tuple_slicer<?> we need for us:
template<class Tuple>
tuple_slicer<Tuple> auto_slice(Tuple&&t){
return {std::forward<Tuple>(t)};
}
The slice is only required on whichever side is smaller, but can be done on both sides (for generic code) if required.
It also works at construction. On the right hand side, it should work with std::arrays and pairs and tuples. On the left hand side, it may not work with arrays, due to requirement to construct with {{}}.
live example
Here is the recursive solution your were originally trying to figure out:
#include <tuple>
// Limit case
template<std::size_t I = 0, typename ...From, typename ...To>
typename std::enable_if<(I >= sizeof...(From) || I >= sizeof...(To))>::type
copy_tuple(std::tuple<From...> const & from, std::tuple<To...> & to) {}
// Recursive case
template<std::size_t I = 0, typename ...From, typename ...To>
typename std::enable_if<(I < sizeof...(From) && I < sizeof...(To))>::type
copy_tuple(std::tuple<From...> const & from, std::tuple<To...> & to)
{
std::get<I>(to) = std::get<I>(from);
copy_tuple<I + 1>(from,to);
}
You do not need std::index_sequence or similar apparatus, and this
solution has two strengths that your accepted one does not:
It will compile, and do the right thing, when from is longer than to: the
excess trailing elements of from are ignored.
It will compile, and do the right thing, when either from or to is an
empty tuple: the operation is a no-op.
Prepend it to this example:
#include <iostream>
int main()
{
std::tuple<int, char> a { 1, 'x' };
std::tuple<int, char, bool> b;
// Copy shorter to longer
copy_tuple(a, b);
std::cout << "b<0> = " << std::get<0>(b) << "\n";
std::cout << "b<1> = " << std::get<1>(b) << "\n";
std::cout << "b<2> = " << std::get<2>(b) << "\n\n";
// Copy longer to shorter
std::get<0>(b) = 2;
std::get<1>(b) = 'y';
copy_tuple(b,a);
std::cout << "a<0> = " << std::get<0>(a) << "\n";
std::cout << "a<1> = " << std::get<1>(a) << "\n\n";
// Copy empty to non-empty
std::tuple<> empty;
copy_tuple(empty,a);
std::cout << "a<0> = " << std::get<0>(a) << "\n";
std::cout << "a<1> = " << std::get<1>(a) << "\n\n";
// Copy non-empty to empty
copy_tuple(a,empty);
return 0;
}
(g++ 4.9/clang 3.5, -std=c++11)
I'm looking at the following problem:
I get strings that are formatted like this:
functionname_parameter1_parameter2_parameter3
otherfunctionname_parameter1_parameter2
.
.
.
and i would like to call the function with the given parameters.
So let's say i have a function test:
void test(int x, float y, std::string z) {}
and i get a message:
test_5_2.0_abc
then i would like the function test to be automatically invoked like this:
test(5, 2.0, "abc");
Do you have any hints on how to accomplish this in C++?
Update: Updated stream_function to fix the argument-evaluation-order problem #Nawaz mentioned in the comments, and also removed the std::function for improved efficiency. Note that the evaluation-order fix only works for Clang, as GCC doesn't follow the standard here. An example for GCC, with manual order-enforcement, can be found here.
This is generally not that easy to accomplish. I wrote a little wrapper class around std::function once that extracts the arguments from a std::istream. Here's an example using C++11:
#include <map>
#include <string>
#include <iostream>
#include <sstream>
#include <functional>
#include <stdexcept>
#include <type_traits>
// for proper evaluation of the stream extraction to the arguments
template<class R>
struct invoker{
R result;
template<class F, class... Args>
invoker(F&& f, Args&&... args)
: result(f(std::forward<Args>(args)...)) {}
};
template<>
struct invoker<void>{
template<class F, class... Args>
invoker(F&& f, Args&&... args)
{ f(std::forward<Args>(args)...); }
};
template<class F, class Sig>
struct stream_function_;
template<class F, class R, class... Args>
struct stream_function_<F, R(Args...)>{
stream_function_(F f)
: _f(f) {}
void operator()(std::istream& args, std::string* out_opt) const{
call(args, out_opt, std::is_void<R>());
}
private:
template<class T>
static T get(std::istream& args){
T t; // must be default constructible
if(!(args >> t)){
args.clear();
throw std::invalid_argument("invalid argument to stream_function");
}
return t;
}
// void return
void call(std::istream& args, std::string*, std::true_type) const{
invoker<void>{_f, get<Args>(args)...};
}
// non-void return
void call(std::istream& args, std::string* out_opt, std::false_type) const{
if(!out_opt) // no return wanted, redirect
return call(args, nullptr, std::true_type());
std::stringstream conv;
if(!(conv << invoker<R>{_f, get<Args>(args)...}.result))
throw std::runtime_error("bad return in stream_function");
*out_opt = conv.str();
}
F _f;
};
template<class Sig, class F>
stream_function_<F, Sig> stream_function(F f){ return {f}; }
typedef std::function<void(std::istream&, std::string*)> func_type;
typedef std::map<std::string, func_type> dict_type;
void print(){
std::cout << "print()\n";
}
int add(int a, int b){
return a + b;
}
int sub(int a, int b){
return a - b;
}
int main(){
dict_type func_dict;
func_dict["print"] = stream_function<void()>(print);
func_dict["add"] = stream_function<int(int,int)>(add);
func_dict["sub"] = stream_function<int(int,int)>(sub);
for(;;){
std::cout << "Which function should be called?\n";
std::string tmp;
std::cin >> tmp;
auto it = func_dict.find(tmp);
if(it == func_dict.end()){
std::cout << "Invalid function '" << tmp << "'\n";
continue;
}
tmp.clear();
try{
it->second(std::cin, &tmp);
}catch(std::exception const& e){
std::cout << "Error: '" << e.what() << "'\n";
std::cin.ignore();
continue;
}
std::cout << "Result: " << (tmp.empty()? "none" : tmp) << '\n';
}
}
Compiles under Clang 3.3 and works as expected (small live example).
Which function should be called?
a
Invalid function 'a'
Which function should be called?
add
2
d
Error: 'invalid argument to stream_function'
Which function should be called?
add
2
3
Result: 5
Which function should be called?
add 2 6
Result: 8
Which function should be called?
add 2
6
Result: 8
Which function should be called?
sub 8 2
Result: 6
It was fun to hack that class together again, hope you enjoy. Note that you need to modify the code a little to work for your example, since C++ IOstreams have whitespace as delimiter, so you'd need to replace all underscores in your message with spaces. Should be easy to do though, after that just construct a std::istringstream from your message:
std::istringstream input(message_without_underscores);
// call and pass 'input'
You pretty much can't, C++ doesn't have any kind of reflection on functions.
The question then is how close you can get. An interface like this is pretty plausible, if it would suit:
string message = "test_5_2.0_abc";
string function_name = up_to_first_underscore(message);
registered_functions[function_name](message);
Where registered_functions is a map<string,std::function<void,string>>, and you have to explicitly do something like:
registered_functions["test"] = make_registration(test);
for each function that can be called in this way.
make_registration would then be a fairly hairy template function that takes a function pointer as a parameter and returns a std::function object that when called splits the string into chunks, checks that there are the right number there, converts each one to the correct parameter type with a boost::lexical_cast, and finally calls the specified function. It would know the "correct type" from the template argument to make_registration -- to accept arbitrarily many parameters this would have to be a C++11 variadic template, but you can fake it with:
std::function<void,string> make_registration(void(*fn)(void));
template <typename T>
std::function<void,string> make_registration(void(*fn)(T));
template <typename T, U>
std::function<void,string> make_registration(void(*fn)(T, U));
// etc...
Dealing with overloads and optional parameters would add further complication.
Although I don't know anything about them, I expect that there are C++ support frameworks out there for SOAP or other RPC protocols, that might contain some relevant code.
What you are looking for is reflection. And it is not possible in C++. C++ is designed with speed in mind. If you require inspection of a library or code and then identify the types in it and invoke methods associated with those types (usually classes) then I am afraid it is not possible in C++.
For further reference you can refer to this thread.
How can I add reflection to a C++ application?
http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTI
Why does C++ not have reflection?
You could parse the string, separate the arguments and send them to the function with no problem, but what you cannot do is reference the function with its name on a string, because the function doesn't have a name anymore at runtime.
You could have a if-else if chain that checks for the function name, and then parse the arguments and call the specific function.
I modified #Xeo's code to work with gcc properly, so it ensures the parameters are pulled in the right order. I'm only posting this since it took me a while to understand the original code and splice in the order-enforcement. Full credit should still go to #Xeo. If I find anything wrong with my implementation I'll come back and edit, but thus far in my testing I haven't seen any problems.
#include <map>
#include <string>
#include <iostream>
#include <sstream>
#include <functional>
#include <stdexcept>
#include <type_traits>
#include <tuple>
template<class...> struct types{};
// for proper evaluation of the stream extraction to the arguments
template<class ReturnType>
struct invoker {
ReturnType result;
template<class Function, class... Args>
invoker(Function&& f, Args&&... args) {
result = f(std::forward<Args>(args)...);
}
};
template<>
struct invoker<void> {
template<class Function, class... Args>
invoker(Function&& f, Args&&... args) {
f(std::forward<Args>(args)...);
}
};
template<class Function, class Sig>
struct StreamFunction;
template<class Function, class ReturnType, class... Args>
struct StreamFunction<Function, ReturnType(Args...)>
{
StreamFunction(Function f)
: _f(f) {}
void operator()(std::istream& args, std::string* out_opt) const
{
call(args, out_opt, std::is_void<ReturnType>());
}
private:
template<class T>
static T get(std::istream& args)
{
T t; // must be default constructible
if(!(args >> t))
{
args.clear();
throw std::invalid_argument("invalid argument to stream_function");
}
return t;
}
//must be mutable due to const of the class
mutable std::istream* _args;
// void return
void call(std::istream& args, std::string*, std::true_type) const
{
_args = &args;
_voidcall(types<Args...>{});
}
template<class Head, class... Tail, class... Collected>
void _voidcall(types<Head, Tail...>, Collected... c) const
{
_voidcall<Tail...>(types<Tail...>{}, c..., get<Head>(*_args));
}
template<class... Collected>
void _voidcall(types<>, Collected... c) const
{
invoker<void> {_f, c...};
}
// non-void return
void call(std::istream& args, std::string* out_opt, std::false_type) const {
if(!out_opt) // no return wanted, redirect
return call(args, nullptr, std::true_type());
_args = &args;
std::stringstream conv;
if(!(conv << _call(types<Args...>{})))
throw std::runtime_error("bad return in stream_function");
*out_opt = conv.str();
}
template<class Head, class... Tail, class... Collected>
ReturnType _call(types<Head, Tail...>, Collected... c) const
{
return _call<Tail...>(types<Tail...>{}, c..., get<Head>(*_args));
}
template<class... Collected>
ReturnType _call(types<>, Collected... c) const
{
return invoker<ReturnType> {_f, c...} .result;
}
Function _f;
};
template<class Sig, class Function>
StreamFunction<Function, Sig> CreateStreamFunction(Function f)
{
return {f};
}
typedef std::function<void(std::istream&, std::string*)> StreamFunctionCallType;
typedef std::map<std::string, StreamFunctionCallType> StreamFunctionDictionary;
This also works with Visual Studio 2013, have not tried earlier versions.