Reputation: 491
I am trying to do something horrible with C++.
I have a function f(void*, ...)
that needs to accept pointers to int
, double
, whatever. I have a lot of constructions like this:
void whatever() {
somelogicHere();
int a;
double b;
char c;
f(&a, &b, &c);
some(a);
actions(b);
onabc(c);
}
And I want to wrap that hanful of actions and variable definitions into some template, and call it like:
myTemplate([](int a, double b, char c) {
some(a);
actions(b);
onabc(c);
});
Small example of what must be done:
template<int S, typename ...A>
void magicTemplate(const char(&fmt)[S], std::function<void(A...)> callback)
{
char format[2 + S];
format[0] = 'O';
format[1 + S] = '\0';
int s = S;
memcpy(format + 1, fmt, S);
// next lines is just a random stuff that does not work
std::tuple<A...> local_arguments;
extractArgs(format, &local_arguments...); //pointers to arguments must be passed
callback(local_arguments...); // arguments must be passed
}
... and call of my template:
magicTemplate("iii", [](int a, double b, char c)
{
std::cout < a << std::endl;
std::cout < b << std::endl;
std::cout < c << std::endl;
});
First thing - my template does not match this argument, and the second thing - I have no idea what must be inside of magicTemplate
's body.
Upvotes: 0
Views: 1067
Reputation: 218323
You might use std::apply
(c++17 but implementable in c++11)
template<int N, typename ... Ts>
void magicTemplate(const char(&fmt)[N], std::function<void(Ts...)> callback)
{
char format[2 + N];
format[0] = 'O';
format[N + 1] = '\0';
memcpy(format + 1, fmt, N);
std::tuple<Ts...> local_arguments;
std::apply([&](auto& ...args){ extractArgs(format, &args...); }, local_arguments);
std::apply(callback, local_arguments);
}
Then, to transform your lambda into std::function
, you might have something like:
template <typename C> struct helper : helper<decltype(&C::operator())> {};
template <typename Ret, typename C, typename ...Ts>
struct helper<Ret (C::*)(Ts...) const> {
using type = Ret(Ts...);
};
template <typename Ret, typename C, typename ...Ts>
struct helper<Ret (C::*)(Ts...)> {
using type = Ret(Ts...);
};
template <typename F>
std::function<typename helper<std::decay_t<F>>::type> as_std_function(F&& f) { return f; }
And so finally:
template<int N, typename F>
void magicTemplateFinal(const char(&fmt)[N], F&& f)
{
magicTemplate(fmt, as_std_function(f));
}
Upvotes: 1
Reputation: 63174
I'm getting some good mileage out of that parameter-detection code :)
Let's get this done first -- we want to get a list of parameter types from the type of whatever we passed to magicTemplate
:
namespace glk {
namespace tmp {
template <class T>
struct type_is {
using type = T;
};
template <class...>
using void_t = void;
// Pack of arbitrary types
template <class...>
struct pack { };
namespace detail_parameters {
template <class F, class = void_t<>>
struct parameters { };
template <class F>
struct parameters<F, void_t<decltype(&F::operator ())>>
: parameters<decltype(&F::operator ())> { };
template <class R, class... Params>
struct parameters<R(Params...)> : type_is<pack<Params...>>{ };
template <class R, class... Params>
struct parameters<R(*)(Params...)> : type_is<pack<Params...>>{ };
template <class T, class R, class... Params>
struct parameters<R(T::*)(Params...)> : type_is<pack<Params...>>{ };
template <class T, class R, class... Params>
struct parameters<R(T::*)(Params...) const> : type_is<pack<Params...>>{ };
}
// Retrieve the parameter list from a functionoid
template <class F>
using parameters = typename detail_parameters::parameters<
std::remove_reference_t<F>
>::type;
}
}
Now glk::tmp::parameters<F>
gives us a glk::tmp::pack<T...>
where each T
corresponds to a parameter. Now, let's suppose we have that, and implement the actual body of magicTemplate
:
template <std::size_t FmtSize, class AndThen, class... Args, std::size_t... ArgsIdx>
void magicTemplate(
char const (&fmt)[FmtSize], AndThen &&andThen,
glk::tmp::pack<Args...>,
std::index_sequence<ArgsIdx...>
) {
std::array<char, FmtSize + 1> fmtStr;
fmtStr[0] = 'O';
std::copy(fmt, fmt + FmtSize, fmtStr.data() + 1);
std::tuple<Args...> args;
std::scanf(fmtStr.data(), &std::get<ArgsIdx>(args)...);
std::forward<AndThen>(andThen)(std::get<ArgsIdx>(args)...);
}
(I have replaced extractArgs
with std::scanf
for testing purposes, since they seem to be quite similar)
Now just a bit of plumbing to actually produce the required std::index_sequence
:
template <std::size_t FmtSize, class AndThen, class... Args>
void magicTemplate(
char const (&fmt)[FmtSize], AndThen &&andThen,
glk::tmp::pack<Args...>
) {
return magicTemplate(
fmt, std::forward<AndThen>(andThen),
glk::tmp::pack<Args...>{},
std::index_sequence_for<Args...>{}
);
}
template <std::size_t FmtSize, class AndThen>
void magicTemplate(char const (&fmt)[FmtSize], AndThen &&andThen) {
return magicTemplate(
fmt, std::forward<AndThen>(andThen),
glk::tmp::parameters<AndThen>{}
);
}
Et voilà! We can call this thing with the exact syntax you wished for. Of course, everything looking remotely like error checking has been left as an exercise for the reader :)
Upvotes: 1