How to generalize this function with variadic templates c++ - c++

I have the following function. It converts two bindings of T0 and T1 to a binding of a tuple<T0,T1>
The function is as follows
template<typename T0, typename T1>
typename RxBinding<std::tuple<T0,T1>>::Ptr
Combine(RxBinding<T0>::Ptr b0, RxBinding<T1>::Ptr b1)
{
using Tuple = std::tuple<T0,T1>;
RxBinding<Tuple>::Ptr binding = makeValueBinding(std::make_tuple(b0->Get(),b1->Get()));
// Break the reference cycle.
auto bindingWeak = std::weak_ptr<RxBinding<Tuple>>(binding);
auto s0 = b0->Subscribe([bindingWeak,b1](T0 const & v0){
auto b = bindingWeak.lock();
if(b)
b->Update(std::make_tuple(v0,b1->Get()));
});
auto s1 = b1->Subscribe([bindingWeak,b0](T1 const & v1){
auto b = bindingWeak.lock();
if(b)
b->Update(std::make_tuple(b0->Get(),v1));
});
auto sN = binding->Subscribe([b0,b1](std::tuple<T0,T1> const & t){
b0->Update(std::get<0>(t));
b1->Update(std::get<1>(t));
});
binding->CleanupWith << s0 << s1 << sN;
return binding;
}
Don't worry too much about what a binding is. Assume they work. I'm looking for a pattern to generalise this using C++11 variadic templates so I can have N bindings as input rather than just two and convert them to a single binding?
template <typename ...T>
typename RxBinding<std::tuple<T...>>::Ptr
Combine( RxBinding<T>::Ptr args...) /* is this possible ?? */
{
using Tuple = std::tuple<T...>;
auto binding = makeValueBinding(std::make_tuple( /* what do do here with args ?? */ ));
// Break the reference cycle.
RxBinding<Tuple>::Ptr bindingWeak = std::weak_ptr<RxBinding<Tuple>>(binding);
// Make N subscriptions b0,b1,....bN with the weak reference above
/* What to do here ?? */
// Make the final subscription
auto sN = binding->Subscribe([](std::tuple<T...> const & t){
// Update the N bindings.
/* what to do here ? */
});
// Add all subscriptions to the cleanup on the final binding
/* not sure what to do here */
return binding;
}

From RxBinding<T>::Ptr T can't be deduced, as it's a non-deduced context because of nested types (see example 1) under Non-deduced contexts on cppreference and godbolt example), so the original example shouldn't have worked with argument deduction. With that in mind having typename RxBinding<Ts>::Ptr ...args will work the same way as it did before (note the syntax having ... before the argument name). I changed the variadic type template to Ts instead of T, to better represent that it's variadic.
With auto binding = makeValueBinding(std::make_tuple( /* what do do here with args ?? */ )); you can use a pack expansion with the pattern args->Get(), so the final line will be
auto binding = makeValueBinding(std::make_tuple(args->Get()...));.
Creation of the variables s0, s1, and so on is not trivial, so I'll get back to it at the end.
To make the final subscribtion, you will need to use a helper function to expand the tuple:
template<typename ...ArgTypes, typename ...Ts, std::size_t ...Ns>
void FinalSubscribeHelper(
std::tuple<ArgTypes...> const &args,
std::tuple<Ts...> const &t,
std::index_sequence<Ns...>
)
{
// using C++17's fold expressions (https://en.cppreference.com/w/cpp/language/fold)
((std::get<Ns>(args)->Update(std::get<Ns>(t))), ...); // we use the comma operator for expansion
return;
// using array initializers for C++11
using ArrayT = int[sizeof...(ArgTypes)];
ArrayT{
((
std::get<Ns>(args)->Update(std::get<Ns>(t)) // this is the pattern
), 0)...
};
return;
}
So the final subscribtion is
auto sN = binding->Subscribe([=](std::tuple<Ts...> const &t){
// Update the N bindings.
FinalSubscribeHelper(std::make_tuple(args...), t, std::make_index_sequence<sizeof...(Ts)>{});
});
For adding all subscriptions to the cleanup you will need another helper function:
template<typename BindingT, typename ...STs, typename SNT, std::size_t ...Ns>
void CleanupHelper(
BindingT const &binding,
std::tuple<Ts...> const &s,
SNT const &sN
std::index_sequence<Ns...>
)
{
// using C++17's fold expressions (https://en.cppreference.com/w/cpp/language/fold)
(binding->CleanupWith << ... << std::get<Ns>(s)) << sN;
return;
// using array initializers for C++11
/*
this only works if
binding->CleanupWith << s0 << s1 << sN;
is equivalent to
binding->CleanupWith << s0;
binding->CleanupWith << s1;
binding->CleanupWith << sN;
*/
using ArrayT = int[sizeof...(ArgTypes)];
ArrayT{
((
binding->CleanupWith << std::get<Ns>(s)
), 0)...
};
binding->CleanupWith << sN;
return;
}
So the final cleanup is
CleanupHelper(binding, s, sN, std::make_index_sequence<sizeof...(Ts)>{});.
Now get back to creating s. To create the callback I assume you want Update to be called as
b->Update(std::make_tuple(/* bM->Get() with M = 0, 1, 2, ..., I-1 */, vI, /* bM->Get() with M = I+1, I+2, ..., N-1 */));. For this you need two index sequences, one from 0 to I-1 and one from I+1 to N-1. For that let's create some type aliases to make the needed std::index_sequence's.
template<std::size_t Offset, typename T>
struct AddOffset;
template<std::size_t Offset, std::size_t ...Ns>
struct AddOffset<Offset, std::index_sequence<Ns...>>
{
using type = std::index_sequence<(Ns + Offset)...>;
};
template<std::size_t Offset, typename T>
using AddOffsetT = typename AddOffset<Offset, T>::type;
// this creates a std::index_sequence with the values
// Start, Start+1, Start+2, ..., End-1
template<std::size_t Start, std::size_t End>
using MakeIndexSequenceInRange = AddOffsetT<Start, std::make_index_sequence<End - Start>>;
To create s you will need a few helper functions:
template<typename BindingT, typename ...ArgTypes, typename VT, std::size_t ...Ns, std::size_t ...Ms>
void SubscribeCallbackHelper(
BindingT const &b,
std::tuple<ArgTypes...> const &args,
VT const &v,
std::index_sequence<Ns...>,
std::index_sequence<Ms...>
)
{
b->Update(std::make_tuple(std::get<Ns>(args)->Get()..., v, std::get<Ms>(args)->Get()...));
}
template<typename BindingWeakT, typename ...ArgTypes, std::size_t ...Ns>
auto CreateS(
BindingWeakT const &bindingWeak,
std::tuple<ArgTypes...> const &args,
std::index_sequence<Ns...>
) -> decltype(std::make_tuple(std::get<Ns>(args)->Subscribe(std::declval<void(*)(ArgTypes const &)>())...))
// I'm not sure this decltype will work, if you have C++14 you should be able to just use auto as a return type
{
return std::make_tuple(
std::get<Ns>(args)->Subscribe([bindingWeak, args](ArgTypes const &v) {
auto b = bindingWeak.lock();
if (b)
SubscribeCallbackHelper(b, args, v, MakeIndexSequenceInRange<0, Ns>{}, MakeIndexSequenceInRange<Ns+1, sizeof...(ArgTypes)>{});
})
);
}
So the creation of s will be
auto s = CreateS(bindingWeak, std::make_tuple(args...), std::make_index_sequence<sizeof...(Ts)>{});

Related

Trying to reduce Big O complexity of compile-time format string parsing

We have a formatting library similar to fmtlib which allows us to format strings like this:
int foo = 1;
double bar = 2.3;
FMT("foo={} bar={}", foo, bar);
We recently added parsing of the format string at compile time, allowing us to specify, eg width,
precision etc directly in the format string
// print foo with width=5, pad with 0; print bar with width 6, precision 2
// as decimal floating point
FMT("foo={:05} bar={:6.2f}", foo, bar);
The way we do this is as follows:
We have a struct which I can use to pass the format string as a template parameter
struct FmtString
{
template<std::size_t N>
constexpr FmtString(const char (&s)[N])
: str(s)
{}
const char* const str;
};
Together with this FmtString and the arguments to be formatted, we use the following somewhat convoluted dance
to generate a std::tuple of FmtSpecT format specifications.
We use the variadic template parameter pack to generate a std::index_sequence:
// use Ts... to create a tuple of the argument types and an std::index_sequence
template<FmtString S, typename... Ts>
struct CompiledFmtSpec
{
using type = decltype(compileFmtSpec<S, std::tuple<Ts...>>(std::index_sequence_for<Ts...> {}));
};
// pull the indices out of the std::index_sequence
template<FmtString S, typename Tuple, std::size_t... Is>
consteval auto compileFmtSpec(std::index_sequence<Is...>)
{
return typename FmtSpecs<S, Tuple, Is...>::type {};
}
We use the std::index_sequence to generate a variadic std::size_t sequence which we can use to
extract each argument from the tuple of arguments, and its respective sequence value
template<FmtString S, typename Tuple, std::size_t... Is>
struct FmtSpecs
{
using type = std::tuple<FmtSpecWrapper<S, std::tuple_element_t<Is, Tuple>, Is>...>;
};
This results in the following tuple:
std::tuple<
FmtSpecWrapper<S, T0, 0>,
FmtSpecWrapper<S, T1, 1>,
FmtSpecWrapper<S, T2, 2>,
...
FmtSpecWrapper<S, Tn, n>>
We can then specialise FmtSpecWrapper for the particular type and parse its supported format spec
Here is the default implementation
template<FmtString S, typename T, std::size_t I>
struct FmtSpecWrapper
{
using type = decltype(parseFmtString<S, I>());
};
template<FmtString S, std::size_t I>
consteval auto parseFmtString()
{
constexpr const char* pos = findFmtSpec(S, I);
constexpr FmtSpec fmt = parseFmtSpec(pos);
return FmtSpecT<fmt.pad, fmt.align_right, fmt.sign, fmt.width, fmt.precision, fmt.type, fmt.spec_len> {};
}
This results in a FmtSpecT specialisation, which has the following NTTPs:
template<char Pad, bool AlignRight, bool Sign, int Width, int Precision, char Type, int SpecLen>
struct FmtSpecT
{
static constexpr char pad = Pad;
static constexpr bool align_right = AlignRight;
static constexpr bool sign = Sign;
static constexpr int width = Width;
static constexpr int precision = Precision;
static constexpr char type = Type;
static constexpr int spec_len = SpecLen;
};
During formatting we can then pull out each FmtSpecT and use the members to format the argument.
The issue I'd like to try and solve is findFmtSpec(S, I).
Its job is to find the I'th format spec from the format string S.
constexpr const char* findFmtSpec(const FmtString& S, std::size_t i)
{
std::size_t curr = 0;
const char* next = S.str;
while (*next)
{
const char c = *next++;
if (c == '{')
{
if (c == *next) // found an escaped brace; {{ or }}
{
++next;
}
else // found the start of the fmt spec
{
if (curr == i)
return next;
++curr;
++next;
}
}
}
throw std::domain_error("too many arguments for format string");
}
My issue is that it is Big O N^2, as it starts from 0 for every argument.
Ideally I would be able to start from the end of the previous format spec and seek forwards to find the next, turning the complexity into O(N).
Any ideas on how I can modify findFmtSpec so that for each i it starts from the return value of the previouc call?
FmtString S is a template parameter, so it is usable in constant expressions.
Therefore all you need to do is write a constexpr function that extracts the specifiers up-front into a suitable container type, for example std::array<std::string_view, N>. You can either enforce directly in the function that the number of specifiers matches sizeof...(Ts), so that N can just be that, or you can write another constexpr function which first counts the number of arguments:
struct FmtString
{
template<std::size_t N>
constexpr FmtString(const char (&s)[N])
: str(s)
{}
const char* const str;
template<FmtString S>
static constexpr auto findFmtSpec = []{
constexpr auto N = []{
// iterate `S.str` here and return number of specifiers
}();
std::array<const char*, N> result;
// iterate `S.str` again and store beginning of format specifiers consecutively in `result`.
return result;
}();
};
Then parseFmtString can just use FmtString::findFmtSpec<S>[I]. I am using a static member variable template instead of a static function template to make sure that the compiler doesn't keep reevaluating an equivalent function call, although I think the compiler should memoize calls with the same arguments. I used nested lambdas for brevity, but you might want to put these into separate functions. There is also no particular reason to have the variable template be a member. It works outside the class as well.
You can also just generate the whole tuple this way:
template<FmtString S, typename... Ts>
static constexpr auto specs = []{
// number of specifiers, can be `sizeof...(Ts)`
// if no specific error handling is required
constexpr auto N = []{
// iterate `S.str` here and return number of specifiers
}();
if constexpr(sizeof...(Ts) != N) {
// handle argument number mismatch
} else {
// specs as values
constexpr auto specs = []{
std::array<FmtSpec, N> specs;
// iterate `S.str` again and store `FmtSpec` for each specifier consecutively in `specs`
return specs;
}();
// lift specs into type (template arguments)
return []<std::size_t... Is>(std::index_sequence<Is...>){
return std::tuple<FmtSpecWrapper<S, Ts, FmtSpecT<specs[Is].pad, /*...*/>>...>{};
}(std::make_index_sequence<N>{});
}
}();

Dispatching from a runtime parameter to different overloads

Suppose I have a set of types :
constexpr std::tuple<int,double,string> my_types;
a set of values to identify them:
constexpr std::array<const char*,3> my_ids = {"int","double","string"}; // const char* instead of string to be constexpr-compatible
and an overload set
template<class T> bool my_fun(my_type complex_object) { /* some treatment depending on type T */ }
I have a manually dispatching function like that:
string my_disp_fun(my_type complex_object) {
const char* id = get_info(complex_object);
using namespace std::string_literals;
if (id == "int"s) {
return my_fun<int>(complex_object);
} else if (id == "double"s) {
return my_fun<double>(complex_object);
} else if (id == "string"s) {
return my_fun<string>(complex_object);
} else {
throw;
}
}
Because I see this pattern coming again and again with a different my_fun every time, I would like to replace it by something like that:
struct my_mapping {
static constexpr std::tuple<int,double,string> my_types;
static constexpr std::array<const char*,3> my_ids = {"int","double","string"}; // const char* instead of string to be constexpr-compatible
}
string my_disp_fun(my_type complex_object) {
const char* id = get_info(complex_object);
return
dispatch<my_mapping>(
id,
my_fun // pseudo-code since my_fun is a template
);
}
How to implement the dispatch function? I am pretty confident it can be done but so far, I can't think of a reasonably nice API that would still be implementable with template metaprograming tricks.
I am sure people already had the need for this kind of problem. Is there a name for this pattern? I don't really know how to even qualify it in succinct technical terms...
Side question: is it related to the pattern matching proposal? I'm not sure because the paper seems more interested in the matching part, not generating branchs from that, right ?
Leverage variant.
template<class T>struct tag_t{using type=T};
template<class T>constexpr tag_t<T> tag={};
template<class...Ts>
using tag_enum = std::variant<tag_t<Ts>...>;
now tag_enum is a type stores a type at runtime as a value. Its runtime representation is an integer (!), but C++ knows that integer represents a specific type.
We now just have to map your strings to integers
using supported_types=tag_enum<int, double, std::string>;
std::unordered_map<std::string, supported_types> name_type_map={
{"int", tag<int>},
{"double", tag<double>},
{"string", tag<std::string>},
};
this map can be built from an array and a tuple if you want, or made global somewhere, or made into a function.
The point is, a mapping of any kind to a tag_enum can be used to auto dispatch a function.
To see how:
string my_disp_fun(my_type complex_object) {
const char* id = get_info(complex_object);
return std::visit( [&](auto tag){
return my_fun<typename decltype(tag)::type>( complex_object );
}, name_type_map[id] };
}
refactoring this to handle whatever level of automation you want should be easy.
If you adopt the convention that you pass T as a tag_t as the first argument it gets even easier to refactor.
#define RETURNS(...)\
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
#define MAKE_CALLER_OF(...) \
[](auto&&...args) \
RETURNS( (__VA_ARGS__)(decltype(args)(args)...) )
now you can easily wrap a template function into an object
template<class F>
auto my_disp_fun(my_type complex_object F f) {
const char* id = get_info(complex_object);
return std::visit( [&](auto tag){
return f( tag, complex_object );
}, name_type_map[id] }; // todo: handle failure to find it
}
then
std::string s = my_disp_fun(obj, MAKE_CALLER_OF(my_fun));
does the dispatch for you.
(In theory we could pass the template parameter in the macro, but the above macros are generically useful, while one that did wierd tag unpacking are not).
Also we can make a global type map.
template<class T>
using type_entry = std::pair<std::string, tag_t<T>>;
#define TYPE_ENTRY_EX(NAME, X) type_entry<X>{ NAME, tag<X> }
#define TYPE_ENTRY(X) TYPE_ENTRY_EX(#X, X)
auto TypeTable = std::make_tuple(
TYPE_ENTRY(int),
TYPE_ENTRY(double),
TYPE_ENTRY_EX("string", std::string)
);
template<class Table>
struct get_supported_types_helper;
template<class...Ts>
struct get_supported_types_helper<std::tuple<type_entry<Ts>...>> {
using type = tag_enum<Ts...>;
};
template<class Table>
using get_supported_types = typename get_supported_types_helper<Table>::type;
From that you can do things like make the unordered map from the TypeTable tuple automatically.
All of this is just to avoid having to mention the supported types twice.
Since your functions have the same signature, you can use a std::map to map the ids to function pointers, eg:
template<class T>
std::string my_fun(my_type complex_object)
{
/* some treatment depending on type T */
return ...;
}
using my_func_type = std::string(*)(my_type);
const std::map<std::string, my_func_type> my_funcs = {
{"int", &my_fun<int>},
{"double", &my_fun<double>},
{"string", &my_fun<std::string>}
};
std::string my_disp_fun(my_type complex_object)
{
const char *id = get_info(complex_object);
auto iter = my_funcs.find(id);
if (iter == my_funcs.end())
throw ...;
return iter->second(complex_object);
}
Demo
I'm not sure that this is what you are looking for. But you can do it without the need to hold an additional array with the types:
// overload visitor trick
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
// deduction guide
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
int main() {
std::tuple<int, double, const char*> tup = {10, 2.5, "hello"};
auto f = overloaded {
[](int arg){std::cout << arg << " + 3 = " << arg + 3 << std::endl;},
[](double arg){std::cout << arg << " * 2 = " << arg * 2 << std::endl;},
[](const std::string& arg){std::cout << "string: " << arg << std::endl;}
};
std::apply([&](const auto&... e){ (f(e), ...);}, tup);
}
Code: http://coliru.stacked-crooked.com/a/3bfdd35f89ceeff9

Ability to multiply two parameter packs in C++ 11 in dot product fashion via variadic templates

template<class...A, class...B> void func(A...arg1,int sz1, int sz2, B...arg2)
{
std::cout << "sizeof parameter pack for A = " << sizeof...(arg1) << std::endl;
std::cout << "sizeof parameter pack for B = " << sizeof...(arg2) << std::endl;
}
using namespace std;
int main(void)
{
func<int,int,int,int>(1,2,3,4,4,4,5,6,7,8);
}
My question is how can i peel the first parameter of each of these packs and multiply them and then sum them with recursive call to reduced parameter pack of both the packs.
Esentially I want to implement : SUM(A[i] * B[i])
You can actually do that without recursion.
template<class...A, class...B> int func(A...arg1, int sz1, int sz2, B...arg2)
{
std::vector<int> p = {(arg1 * arg2)...};
return std::accumulate(p.begin(), p.end(), 0);
}
There is no needs of recursion (if you don't want a C++11 constexpr returning function).
Suggestion: instead of a couple of variadic arguments use a couple of C-style arrays as follows
template <typename T, std::size_t N>
T func (T const (&a1)[N], T const (&a2)[N])
{
T ret {};
for ( auto ui { 0u } ; ui < N ; ++ui )
ret += a1[ui] * a2[ui];
return ret;
}
This way you have: (1) an homogeneous T type instead a couple of variadic type lists and (2) that the two list must be of the same size N or the function doesn't match. I suppose they are advantages if you want a sum(a[i] * b[i])
The function should be called with a couple of initialization list (that is: wrapping in brackets the two lists)
// print 70
std::cout << func({1, 2, 3, 4}, {5, 6, 7, 8}) << std::endl;
Different if you need a constexpr function: the func() can be constexpr starting from C++14 but not in C++11.
If you want a C++11 constexpr function you need it recursive but there no need of "peel the first parameter of each of these packs": you can use and index (with a default starting value of zero)
template <typename T, std::size_t N>
constexpr T func (T const (&a1)[N], T const (&a2)[N], std::size_t pos = 0u)
{
return pos < N
? a1[pos] * a2[pos] + func(a1, a2, pos+1u)
: T{0};
}
-- EDIT --
The OP says
My whole aim is to do this using parameter packs only :)
I see...
Well, I suppose that, in C++11, you can use trick of the initialization of an unused array
template <typename ... As, typename ... Bs>
auto func (As ... args1 ,int, int, Bs ...args2)
-> typename std::common_type<As..., Bs...>::type
{
using unused = int[];
typename std::common_type<As..., Bs...>::type ret{};
(void)unused { 0, ( ret += args1 * args2, 0 ) ... };
return ret;
}
If you can use C++17, you can write
template <typename ... As, typename ... Bs>
auto func (As ... args1 ,int, int, Bs ...args2)
{
return ( (args1 * args2) + ... );
}

How to remove metaprogramming recursion with Boost Hana

I'm trying to create a bitset according to the type send to the function. But let's reduce the test case a little.
Warning : I'm using auto gcc extension for this example, I don't need to use template parameter.
namespace hana = boost::hana;
constexpr decltype(auto) rec(auto i, auto max, auto f, auto returnValue) {
return returnValue |= f(i);
if constexpr (i < max) //"infinite" loop if no constexpr
return rec(i + hana::size_c<1>, max, f, returnValue);
else
return returnValue;
}
constexpr decltype(auto) foo(auto ct, auto ... type) {
constexpr auto tuple = hana::make_tuple(type...);
constexpr unsigned long returnValue = 0L;
constexpr auto f = [tuple, ct] (auto i) {
if (hana::contains(tuple, ct[i]))
return 0 << decltype(i)::value;
else
return 0;
};
return rec(hana::size_c<0>, hana::size_c<3>, f, returnValue);
}
struct T1 {};
struct T2 {};
struct T3 {};
int main () {
constexpr auto t1 = hana::type_c<T1>;
constexpr auto t2 = hana::type_c<T2>;
constexpr auto t3 = hana::type_c<T3>;
constexpr auto ct = hana::make_tuple(t1, t2, t3);
constexpr auto test = foo(ct, t1, t2);
}
Seems like my tuple is not considered Searchable, but if I try the same hana::contains outside the lambda I got no problem.
The whole error is huge so check it there : live demo
By the way, I tried to do this with a for loop but failed. Do you know a good way of doing this kind of things in C++17/20 ?
The error is caused by an out of bounds access caused by the use of manual recursion. Part of the purpose of functional programming is to provide constructs to eliminate the possibility of these kinds of mistakes.
Here are a few examples, but it is recommended to take a look at the manual for the concept hana::Foldable as it is really foundational in using Boost.Hana.
hana::fold_left hides the recursion for you and can reduce the amount of recursive calls via fast-tracking:
constexpr decltype(auto) foo = [](auto ct, auto ... type) {
constexpr auto tuple = hana::make_tuple(type...);
return hana::fold_left(hana::make_range(hana::size_c<0>, hana::size_c<3>), 0L,
[tuple, ct](auto returnValue, auto i)
{
// returnValue param is not constexpr
if (hana::contains(tuple, ct[i])) {
return returnValue | (1 << decltype(i)::value);
}
else
{
return returnValue;
}
}
);
};
hana::fold_left example
hana::unpack eliminates recursion altogether using variadic pack expansion:
constexpr decltype(auto) foo = [](auto ct, auto ... type) {
constexpr auto tuple = hana::make_tuple(type...);
auto f = [tuple, ct](auto i)
{
return hana::contains(tuple, ct[i]) ? (1 << decltype(i)::value) : 0;
};
return hana::unpack(hana::make_range(hana::size_c<0>, hana::size_c<3>),
[f](auto ...i) { return (f(i) | ...); }
);
};
hana::unpack example

How can implement dynamic function call with C++11 and C++14?

Here is code that I hope explains what I want to achieve.
vector<int> ints;
vector<double> doubles;
struct Arg {
enum Type {
Int,
Double
};
Type type;
int index;
};
template <typename F>
void Call(const F& f, const vector<Arg>& args) {
// TODO:
// - First assert that count and types or arguments of <f> agree with <args>.
// - Call "f(args)"
}
// Example:
void copy(int a, double& b) {
b = a;
}
int test() {
Call(copy, {{Int, 3}, {Double, 2}}); // copy(ints[3], double[2]);
}
Can this be done in C++11 ?
If yes, can the solution be simplified in C++14 ?
I'd do this in two steps.
First, I'd wrap f in an object able to understand Arg-like parameters, and generate errors on failure. For simplicity, suppose we throw.
This is a bit simpler than your Arg to be understood at this layer, so I might translate Arg into MyArg:
struct MyArg {
MyArg(MyArg const&)=default;
MyArg(int* p):i(p){}
MyArg(double* p):d(p){}
MyArg(Arg a):MyArg(
(a.type==Arg::Int)?
MyArg(&ints.at(a.index)):
MyArg(&doubles.at(a.index))
) {}
int * i = nullptr;
double* d = nullptr;
operator int&(){ if (!i) throw std::invalid_argument(""); return *i; }
operator double&(){ if (!d) throw std::invalid_argument(""); return *d; }
};
We map void(*)(Ts...) to std::function<void(MyArg, MyArg, MyArg)> like this:
template<class T0, class T1>using second_type = T1;
template<class...Ts>
std::function<void( second_type<Ts,MyArg>... )> // auto in C++14
my_wrap( void(*f)(Ts...) ) {
return [f](second_type<Ts,MyArg>...args){
f(args...);
};
}
now all that is left is counting function parameter count vs vector size count, and unpacking the std::vector into a function call.
The last looks like:
template<class...Ts, size_t...Is>
void call( std::function<void(Ts...)> f, std::index_sequence<Is...>, std::vector<Arg> const& v ) {
f( v[Is]... );
}
template<class...Ts>
void call( std::function<void(Ts...)> f, std::vector<Arg> const& v ) {
call( std::move(f), std::index_sequence_for<Ts...>{}, v );
}
where index_sequence and index_sequence_for are C++14, but equivalents can be implemented in C++11 (there are many implementations on stack overflow).
So we end up with something like:
template<class...Ts>
void Call( void(*pf)(Ts...), std::vector<Arg> const& v ) {
if (sizeof...(Ts)>v.size())
throw std::invalid_argument("");
auto f = my_wrap(pf);
call( std::move(f), v );
}
Dealing with the throws is left as an exercise, as is handling return values.
This code has not been compiled or tested, but the design should be sound. It only supports calling function pointers -- calling generalized callable objects is tricky, because counting how many arguments they want (of type int or double) is tricky. If you passed in how many arguments they want as a compile-time constant, it is easy. You could also build a magic switch that handles counts up to some constant (10, 20, 1000, whatever), and dispatch the runtime length of the vector into a compile time constant that throws on a argument length mismatch.
This is trickier.
The hard coded pointers sort of suck.
template<class...Ts>struct types{using type=types;};
template<size_t I> using index=std::integral_constant<size_t, I>;
template<class T, class types> struct index_in;
template<class T, class...Ts>
struct index_in<T, types<T,Ts...>>:
index<0>
{};
template<class T, class T0, class...Ts>
struct index_in<T, types<T0,Ts...>>:
index<1+index_in<T, types<Ts...>>{}>
{};
is a package of types.
Here is how we can store buffers:
template<class types>
struct buffers;
template<class...Ts>
struct buffers<types<Ts...>> {
struct raw_view {
void* start = 0;
size_t length = 0;
};
template<class T>
struct view {
T* start = 0;
T* finish = 0;
view(T* s, T* f):start(s), finish(f) {}
size_t size() const { return finish-start; }
T& operator[](size_t i)const{
if (i > size()) throw std::invalid_argument("");
return start[i];
}
}
std::array< raw_view, sizeof...(Ts) > views;
template<size_t I>
using T = std::tuple_element_t< std::tuple<Ts...>, I >;
template<class T>
using I = index_of<T, types<Ts...> >;
template<size_t I>
view<T<I>> get_view() const {
raw_view raw = views[I];
if (raw.length==0) { return {0,0}; }
return { static_cast<T<I>*>(raw.start), raw.length/sizeof(T) };
}
template<class T>
view<T> get_view() const {
return get_view< I<T>{} >();
}
template<class T>
void set_view( view<T> v ) {
raw_view raw{ v.start, v.finish-v.start };
buffers[ I<T>{} ] = raw;
}
};
now we modify Call:
template<class R, class...Args, size_t...Is, class types>
R internal_call( R(*f)(Args...), std::vector<size_t> const& indexes, buffers<types> const& views, std::index_sequence<Is...> ) {
if (sizeof...(Args) != indexes.size()) throw std::invalid_argument("");
return f( views.get_view<Args>()[indexes[Is]]... );
}
template<class R, class...Args, size_t...Is, class types>
R Call( R(*f)(Args...), std::vector<size_t> const& indexes, buffers<types> const& views ) {
return internal_call( f, indexes, views, std::index_sequence_for<Args...>{} );
}
which is C++14, but most components can be translated to C++11.
This uses O(1) array lookups, no maps. You are responsible for populating buffers<types> with the buffers, sort of like this:
buffers<types<double, int>> bufs;
std::vector<double> d = {1.0, 3.14};
std::vector<int> i = {1,2,3};
bufs.set_view<int>( { i.data(), i.data()+i.size() } );
bufs.set_view<double>( { d.data(), d.data()+d.size() } );
parameter mismatch counts and index out of range generate thrown errors. It only works with raw function pointers -- making it work with anything with a fixed (non-template) signature is easy (like a std::function).
Making it work with an object with no signature is harder. Basically instead of relying on the function called for the arguments, you instead build the cross product of the types<Ts...> up to some fixed size. You build a (large) table of which of these are valid calls to the passed in call target (at compile time), then at run time walk that table and determine if the arguments passed in are valid to call the object with.
It gets messy.
This is why my above version simply asks for indexes, and deduces the types from the object being called.
I have a partial solution, using C++11 grammar.
First I make a function overloader accepting arbitrator kinds of arguments
template< typename Function >
struct overloader : Function
{
overloader( Function const& func ) : Function{ func } {}
void operator()(...) const {}
};
template< typename Function >
overloader<Function> make_overloader( Function const& func )
{
return overloader<Function>{ func };
}
then, using the overloader to deceive the compiler into believing the following code ( in switch-case block )is legal:
template <typename F>
void Call( F const& f, const vector<Arg>& args )
{
struct converter
{
Arg const& arg;
operator double&() const
{
assert( arg.type == Double );
return doubles[arg.index];
}
operator int() const
{
assert( arg.type == Int );
return ints[arg.index];
}
converter( Arg const& arg_ ): arg( arg_ ) {}
};
auto function_overloader = make_overloader( f );
unsigned long const arg_length = args.size();
switch (arg_length)
{
case 0 :
function_overloader();
break;
case 1 :
function_overloader( converter{args[0]} );
break;
case 2 :
function_overloader( converter{args[0]}, converter{args[1]} );
break;
case 3 :
function_overloader( converter{args[0]}, converter{args[1]}, converter{args[2]} );
break;
/*
case 4 :
.
.
.
case 127 :
*/
}
}
and test it this way:
void test_1()
{
Call( []( int a, double& b ){ b = a; }, vector<Arg>{ Arg{Int, 3}, Arg{Double, 2} } );
}
void test_2()
{
Call( []( double& b ){ b = 3.14; }, vector<Arg>{ Arg{Double, 0} } );
}
void my_copy( int a, double& b, double& c )
{
b = a;
c = a+a;
}
void test_3()
{
//Call( my_copy, vector<Arg>{ Arg{Int, 4}, Arg{Double, 3}, Arg{Double, 1} } ); // -- this one does not work
Call( []( int a, double& b, double& c ){ my_copy(a, b, c); }, vector<Arg>{ Arg{Int, 4}, Arg{Double, 3}, Arg{Double, 1} } );
}
the problems with this solution is:
g++5.2 accept it, clang++6.1 doesn's
when the argument(s) of function Call is/are not legal, it remains silent
the first argument of function Call cannot be a C-style function, one must wrap that into a lambda object to make it work.
the code is available here - http://melpon.org/wandbox/permlink/CHZxVfLM92h1LACf -- for you to play with.
First of all, you need some mechanism to register your argument values that are later referenced by some type and an index:
class argument_registry
{
public:
// register a range of arguments of type T
template <class T, class Iterator>
void register_range(Iterator begin, Iterator end)
{
// enclose the range in a argument_range object and put it in our map
m_registry.emplace(typeid(T), std::make_unique<argument_range<T, Iterator>>(begin, end));
}
template <class T>
const T& get_argument(size_t idx) const
{
// check if we have a registered range for this type
auto itr = m_registry.find(typeid(T));
if (itr == m_registry.end())
{
throw std::invalid_argument("no arguments registered for this type");
}
// we are certain about the type, so downcast the argument_range object and query the argument
auto range = static_cast<const argument_range_base1<T>*>(itr->second.get());
return range->get(idx);
}
private:
// base class so we can delete the range objects properly
struct argument_range_base0
{
virtual ~argument_range_base0(){};
};
// interface for querying arguments
template <class T>
struct argument_range_base1 : argument_range_base0
{
virtual const T& get(size_t idx) const = 0;
};
// implements get by querying a registered range of arguments
template <class T, class Iterator>
struct argument_range : argument_range_base1<T>
{
argument_range(Iterator begin, Iterator end)
: m_begin{ begin }, m_count{ size_t(std::distance(begin, end)) } {}
const T& get(size_t idx) const override
{
if (idx >= m_count)
throw std::invalid_argument("argument index out of bounds");
auto it = m_begin;
std::advance(it, idx);
return *it;
}
Iterator m_begin;
size_t m_count;
};
std::map<std::type_index, std::unique_ptr<argument_range_base0>> m_registry;
};
Than we define a small type to combine a type and a numerical index for referencing arguments:
typedef std::pair<std::type_index, size_t> argument_index;
// helper function for creating an argument_index
template <class T>
argument_index arg(size_t idx)
{
return{ typeid(T), idx };
}
Finally, we need some template recursion to go through all expected arguments of a function, check if the user passed an argument of matching type and query it from the registry:
// helper trait for call function; called when there are unhandled arguments left
template <bool Done>
struct call_helper
{
template <class FuncRet, class ArgTuple, size_t N, class F, class... ExpandedArgs>
static FuncRet call(F func, const argument_registry& registry, const std::vector<argument_index>& args, ExpandedArgs&&... expanded_args)
{
// check if there are any arguments left in the passed vector
if (N == args.size())
{
throw std::invalid_argument("not enough arguments");
}
// get the type of the Nth argument
typedef typename std::tuple_element<N, ArgTuple>::type arg_type;
// check if the type matches the argument_index from our vector
if (std::type_index{ typeid(arg_type) } != args[N].first)
{
throw std::invalid_argument("argument of wrong type");
}
// query the argument from the registry
auto& arg = registry.get_argument<arg_type>(args[N].second);
// add the argument to the ExpandedArgs pack and continue the recursion with the next argument N + 1
return call_helper<std::tuple_size<ArgTuple>::value == N + 1>::template call<FuncRet, ArgTuple, N + 1>(func, registry, args, std::forward<ExpandedArgs>(expanded_args)..., arg);
}
};
// helper trait for call function; called when there are no arguments left
template <>
struct call_helper<true>
{
template <class FuncRet, class ArgTuple, size_t N, class F, class... ExpandedArgs>
static FuncRet call(F func, const argument_registry&, const std::vector<argument_index>& args, ExpandedArgs&&... expanded_args)
{
if (N != args.size())
{
// unexpected arguments in the vector
throw std::invalid_argument("too many arguments");
}
// call the function with all the expanded arguments
return func(std::forward<ExpandedArgs>(expanded_args)...);
}
};
// call function can only work on "real", plain functions
// as you could never do dynamic overload resolution in C++
template <class Ret, class... Args>
Ret call(Ret(*func)(Args...), const argument_registry& registry, const std::vector<argument_index>& args)
{
// put the argument types into a tuple for easier handling
typedef std::tuple<Args...> arg_tuple;
// start the call_helper recursion
return call_helper<sizeof...(Args) == 0>::template call<Ret, arg_tuple, 0>(func, registry, args);
}
Now you can use it like this:
int foo(int i, const double& d, const char* str)
{
printf("called foo with %d, %f, %s", i, d, str);
// return something
return 0;
}
int main()
{
// prepare some arguments
std::vector<int> ints = { 1, 2, 3 };
std::vector<double> doubles = { 10., 20., 30. };
std::vector<const char*> str = { "alpha", "bravo", "charlie" };
// register them
argument_registry registry;
registry.register_range<int>(ints.begin(), ints.end());
registry.register_range<double>(doubles.begin(), doubles.end());
registry.register_range<const char*>(str.begin(), str.end());
// call function foo with arguments from the registry
return call(foo, registry, {arg<int>(2), arg<double>(0), arg<const char*>(1)});
}
Live example: http://coliru.stacked-crooked.com/a/7350319f88d86c53
This design should be open for any argument type without the need to list all the supported types somewhere.
As noted in the code comments, you cannot call any callable object like this in general, because overload resolution could never be done at runtime in C++.
Instead of clarifying the question, as I requested, you have put it up for bounty. Except if that really is the question, i.e. a homework assignment with no use case, just exercising you on general basic programming, except for that only sheer luck will then give you an answer to your real question: people have to guess about what the problem to be solved, is. That's the reason why nobody's bothered, even with the bounty, to present a solution to the when-obvious-errors-are-corrected exceedingly trivial question that you literally pose, namely how to do exactly this:
vector<int> ints;
vector<double> doubles;
struct Arg {
enum Type {
Int,
Double
};
Type type;
int index;
};
template <typename F>
void Call(const F& f, const vector<Arg>& args) {
// TODO:
// - First assert that count and types or arguments of <f> agree with <args>.
// - Call "f(args)"
}
// Example:
void copy(int a, double& b) {
b = a;
}
int test() {
Call(copy, {{Int, 3}, {Double, 2}}); // copy(ints[3], double[2]);
}
In C++11 and later one very direct way is this:
#include <assert.h>
#include <vector>
using std::vector;
namespace g {
vector<int> ints;
vector<double> doubles;
}
struct Arg {
enum Type {
Int,
Double
};
Type type;
int index;
};
template <typename F>
void Call(const F& f, const vector<Arg>& args)
{
// Was TODO:
// - First assert that count and types or arguments of <f> agree with <args>.
assert( args.size() == 2 );
assert( args[0].type == Arg::Int );
assert( int( g::ints.size() ) > args[0].index );
assert( args[1].type == Arg::Double );
assert( int( g::doubles.size() ) > args[1].index );
// - Call "f(args)"
f( g::ints[args[0].index], g::doubles[args[1].index] );
}
// Example:
void copy(int a, double& b)
{
b = a;
}
auto test()
{
Call(copy, {{Arg::Int, 3}, {Arg::Double, 2}}); // copy(ints[3], double[2]);
}
namespace h {}
auto main()
-> int
{
g::ints = {000, 100, 200, 300};
g::doubles = {1.62, 2.72, 3.14};
test();
assert( g::doubles[2] == 300 );
}
There are no particularly relevant new features in C++14.
I propose this answer following my comment on your question. Seeing that in the requirements, you stated:
Preferably we should not be required to create a struct that
enumerates all the types we want to support.
It could suggests you would like to get rid of the type enumerator in your Arg structure. Then, only the value would be left: then why not using plain C++ types directly, instead of wrapping them ?
It assumes you then know all your argument types at compile time
(This assumption could be very wrong, but I did not see any requirement in your question preventing it. I would be glad to rewrite my answer if you give more details).
The C++11 variadic template solution
Now to the solution, using C++11 variadic templates and perfect forwarding. In a file Call.h:
template <class F, class... T_Args>
void Call(F f, T_Args &&... args)
{
f(std::forward<T_Args>(args)...);
}
Solution properties
This approach seems to satisfy all your explicit requirements:
Works with C++11 standard
Checks that count and types or arguments of f agress with args.
It actually does that early, at compile time, instead of a possible runtime approach.
No need to manually enumerate the accepted types (actually works with any C++ type, be it native or user defined)
Not in your requirement, but nice to have:
Very compact, because it leverage a native features introduced in C++11.
Accepts any number of arguments
The type of the argument and the type of the corresponding f parameter do not have to match exactly, but have to be compatible (exactly like a plain C++ function call).
Example usage
You could test it in a simple main.cpp file:
#include "Call.h"
#include <iostream>
void copy(int a, double& b)
{
b = a;
}
void main()
{
int a = 5;
double b = 6.2;
std::cout << "b before: " << b << std::endl;
Call(copy, a, b);
std::cout << "b now: " << b << std::endl;
}
Which would print:
b before: 6.2
b now: 5