best way to set a bitset with boolean values - c++

Suppose I have 3 bool type values
bool canwalk=true;
bool cantalk=false;
bool caneat=false;
I would like to set a bitset denoting the three
std::bitset<3> foo;
How can I construct a bitset using the boolean values?
I want to do something like this
std::bitset<3> foo(canWalk,cantalk,caneat); //giving me 100

Following the example of Shivendra Agarwal, but using the constructor that receive an unsigned long long, I propose the following variadic template function (to be more generic)
template <typename ... Args>
unsigned long long getULL (Args ... as)
{
using unused = int[];
unsigned long long ret { 0ULL };
(void) unused { 0, (ret <<= 1, ret |= (as ? 1ULL : 0ULL), 0)... };
return ret;
}
that permit the initialization of foo as follows
std::bitset<3> foo{ getULL(canwalk, cantalk, caneat) };
This works only if the dimension of the std::bitset isn't grater of the number of bits in an unsigned long long (with 3 whe are surely safe).
The following is a full working example
#include <bitset>
#include <iostream>
template <typename ... Args>
unsigned long long getULL (Args ... as)
{
using unused = int[];
unsigned long long ret { 0ULL };
(void) unused { 0, (ret <<= 1, ret |= (as ? 1ULL : 0ULL), 0)... };
return ret;
}
int main()
{
bool canwalk=true;
bool cantalk=false;
bool caneat=false;
std::bitset<3> foo{ getULL(canwalk, cantalk, caneat) };
std::cout << foo << std::endl;
}

IMHO, an initialization of type
std::bitset<3> foo(canWalk, cantalk, caneat);
is dangerous (error prone) because require that the template argument of std::bitset (3, in the example) correspond to the number of argument of the initialization.
I propose the creation of a "make" function (following the consolidated example of std::pair(), std::tuple(), std::make_unique(), std::make_shared) where the type and the number of arguments fix the returned type.
So I propose the following makeBitSet() function that return a std::bitset<N> where N is the number of the arguments
template <typename ... Args>
std::bitset<sizeof...(Args)> makeBitSet (Args ... as)
{
using unused = bool[];
std::bitset<sizeof...(Args)> ret;
std::size_t ui { ret.size() };
(void) unused { true, (ret.set(--ui, as), true)... };
return ret;
}
The function can be used as follows
std::bitset<3> foo{ makeBitSet(canwalk, cantalk, caneat) };
but also (better, IMHO), using the C++11 auto,
auto foo = makeBitSet(canwalk, cantalk, caneat);
Observe that, starting from C++14, makeBitSet() can use the returning auto type
template <typename ... Args>
auto makeBitSet (Args ... as)
{
// ...
avoiding the annoying std::bitset<sizeof...(Args)> redundancy.
Moreover, starting from C++17, you can use template folding and, throwing away the unused array (and the corresponding using declaration), the makeBitSet() can be simplified as [EDIT: modified, to improve performances, following a suggestion from Mooing Duck (thanks!)]
template <typename ... Args>
auto makeBitSet (Args ... as)
{
std::bitset<sizeof...(Args)> ret;
std::size_t ui { ret.size() };
( ret.set(--ui, as), ... );
return ret;
}
The following is a full working C++11 example
#include <bitset>
#include <iostream>
template <typename ... Args>
std::bitset<sizeof...(Args)> makeBitSet (Args ... as)
{
using unused = bool[];
std::bitset<sizeof...(Args)> ret;
std::size_t ui { ret.size() };
(void) unused { true, (ret.set(--ui, as), true)... };
return ret;
}
int main()
{
bool canwalk { true };
bool cantalk { false };
bool caneat { false };
auto foo = makeBitSet(canwalk, cantalk, caneat);
std::cout << foo << std::endl;
}

Introduce a new api that can give you string input that bitset accepts in parameter.
to be more generic, recommendation will be to use bool array or [std::vector<bool>][1] to get rid of these variable parameters in getString()
inline std::string getString(bool canwalk, bool canTalk, bool canEat)
{
std::stringstream input;
str << canwalk?1:0 << cantalk?1:0 << caneat?1:0;
return input.str();
}
now can define bitset as:
std::bitset<3> foo (getString(canwalk, canTalk, canEat));

You basically need a builder that will build an initial value from your boolean set to pass to the constructor of std::bitset. You can do this at compile time (as opposed to runtime) via variadic templates, like so:
template <unsigned long long initialValue>
constexpr unsigned long long bitset_value_builder_impl() { return initialValue; }
template <unsigned long long initialValue, typename First, typename ... Args>
constexpr unsigned long long bitset_value_builder_impl(First &&first, Args &&...args) {
return first ?
bitset_value_builder_impl< (initialValue | (1UL<<sizeof...(args)) ), Args...>(std::forward<Args>(args)...) :
bitset_value_builder_impl< (initialValue & ~(1UL<<sizeof...(args)) ), Args...>(std::forward<Args>(args)...);
}
template <typename First, typename ... Args>
constexpr unsigned long long bitset_value_builder(First &&first, Args &&...args) {
return bitset_value_builder_impl<0, First, Args...>(std::forward<First>(first), std::forward<Args>(args)...);
}
int main()
{
bool canwalk=true;
bool cantalk=false;
bool caneat=false;
std::bitset<3> bits{bitset_value_builder(canwalk, cantalk, caneat)};
std::cout << bits << std::endl; //100
}

Related

How to remove redundancy from and add flexibility to template parameters?

I want to use constexpr, compile time generated std::arrays for fast value-lookup instead of lengthy runtime calculations. For that I drafted a templated constexprfunction that will be executed at compile time.
Please see the following example code, which allows for ultrafast access to Triangle and Fibonacci numbers and factorials.
#include <iostream>
#include <utility>
#include <array>
constexpr size_t ArraySize = 20u;
// Some generator functions -------------------------------------------------------------
constexpr size_t getTriangleNumber(size_t row) noexcept {
size_t sum{};
for (size_t i{ 1u }; i <= row; i++) sum += i;
return sum;
}
constexpr unsigned long long getFibonacciNumber(size_t index) noexcept {
unsigned long long f1{ 0ull }, f2{ 1ull }, f3{};
while (index--) { f3 = f2 + f1; f1 = f2; f2 = f3; }
return f2;
}
constexpr unsigned long long getFactorial(size_t index) noexcept {
unsigned long long result{ 1 };
while (index > 0) { result *= index; --index; }
return result;
}
// Generate a std::array with n elements of a given type and a generator function -------
template <typename DataType, DataType(*generator)(size_t), size_t... ManyIndices>
constexpr auto generateArray(std::integer_sequence<size_t, ManyIndices...>) noexcept {
return std::array<DataType, sizeof...(ManyIndices)>{ { generator(ManyIndices)... } };
}
// The arrays ---------------------------------------------------------------------------
constexpr auto TriangleNumber = generateArray<size_t, getTriangleNumber>(std::make_integer_sequence<size_t, ArraySize>());
constexpr auto FibonacciNumber = generateArray<unsigned long long, getFibonacciNumber>(std::make_integer_sequence<size_t, ArraySize>());
constexpr auto Factorial = generateArray<unsigned long long, getFactorial>(std::make_integer_sequence<size_t, ArraySize>());
// Some debug test driver code
int main() {
for (const auto t : TriangleNumber) std::cout << t << ' '; std::cout << '\n';
for (const auto f : FibonacciNumber) std::cout << f << ' '; std::cout << '\n';
for (const auto f : Factorial) std::cout << f << ' '; std::cout << '\n';
return 0;
}
As you can see. The template uses a parameter "DataType". In my opinion this is redundant. This is always the return type of the generator function. And it will also determine the data type for the std::array
So, how can we eliminate this redundancy and just use the type given by the generator function?
Additionally. The functions parameter is always size_t. There is also a redundancy and it is also not very flexible. The type of "ManyIndices" and the function parameter are always the same. So, no need to write that double.
Regarding flexibility. If I want to use a generator function with a different parameter data type, say, unsigned long long as in
constexpr unsigned long long factorial(unsigned long long n) noexcept {
return n == 0ull ? 1ull : n * factorial(n - 1ull);
}
I cannot do that. So, basically everything should be deduced from the generators functions signature.
This is also valid for the lines like
constexpr auto Factorial = generateArray<unsigned long long, getFactorial>(std::make_integer_sequence<size_t, ArraySize>());
Here, size_t is also the type of the parameter of the given function.
So, how eliminate redundancy and add flexibility?
DataType can be deduced from passed generator, use std::declval.
std::integer_sequence can be replaced by std::index_sequence.
Size for calculation must be provided explicitly.
template <typename GEN, size_t ... Indices>
constexpr auto generateArray2Helper(GEN gen, std::index_sequence<Indices...>) {
return std::array<decltype(std::declval<GEN>()(size_t{})), sizeof...(Indices)>{ gen(Indices)... };
}
template <size_t N, typename GEN>
constexpr auto generateArray2(GEN gen) {
return generateArray2Helper(gen, std::make_index_sequence<N>());
}
// The arrays ---------------------------------------------------------------------------
constexpr auto TriangleNumber = generateArray2<ArraySize>(getTriangleNumber);
constexpr auto FibonacciNumber = generateArray2<ArraySize>(getFibonacciNumber);
constexpr auto Factorial = generateArray2<ArraySize>(getFactorial);
Demo
c++20 version:
template<std::size_t...Is>
constexpr auto index_over(auto f, std::index_sequence<Is...>){
return f(std::integral_constant<std::size_t,Is>{}...);
}
template<auto N>
constexpr auto index_upto(auto f){
return index_over(f, std::make_index_sequence<N>{});
}
template<auto size>
constexpr auto gen_array(auto f){
return index_upto<size>([&](auto...Is){
return std::array{f(Is)...};
});
}

How can I do a recursion on C++ variadic templates?

Suppose I want to define a C++ function that has no Input Parameters within bracket, but within the <> argument. I have a Parameter pack as Input Arguments. Meaning that I have to write, for example, a function
int calculate<args...>()
{
return 1 + calculate<some_arg,args...>();
}
And also a base case implementation I have to give. However, I am confronted with a lot of Compiler Errors, because I don't know how to write such a form of recursion out properly. What I have to write before the above function declaration?
template<int... args>
(if data type of args is int; any other data type is also possible in a similar way)? Or what I have to write to avoid Compiler Errors? I also tried
template<int some_arg,int... args>
But I don't know also how to deal with variadic templates (how to unpack them). Any help?
EDIT:
My attempt for one Special case
template<bool... dg>
int calculate<0>()
{
return 1;
}
Error message for this is:
error: expected initializer before ‘<’ token
But I don't know also how to deal with variadic templates (how to unpack them). Any help?
As of C++17 you needn't resort to recursion, but can use pack expansion:
#include <iostream>
template<int ...Args>
constexpr int calculate() {
return (Args + ...);
}
int main() {
std::cout << calculate<1, 2, 3>(); // 6
}
If you want to allow other types of non-type template parameters, you can make use of a placeholder type (auto) for non-type template parameters, also a C++17 feature:
template<auto ...Args>
constexpr auto calculate() {
return (Args + ...);
}
As you cannot partially specialize function templates, you will have to use delegation to a class template if you want to provide different implementations for different specializations:
#include <iostream>
#include <ios>
template<auto ...Args>
struct calculate_impl {
static constexpr auto calc() { return (Args + ...); }
};
template<bool ...Args>
struct calculate_impl<Args...> {
static constexpr bool calc() { return (Args && ...); }
};
template<auto ...Args>
constexpr auto calculate() {
return calculate_impl<Args...>::calc();
}
int main() {
std::cout << calculate<1, 2, 3>(); // 6
std::cout << std::boolalpha
<< "\n" << calculate<false,true>() // false
<< "\n" << calculate<true, true>(); // true
}
If you're using C++17+: refer to dfrib's answer
Here's how you would implement a function to add the elements of a parameter pack using template recursion
template<int arg>
constexpr int add()
{
return arg;
}
template<int arg1, int arg2, int... args>
constexpr int add()
{
return arg1 + add<arg2, args...>();
}
Regarding if you wanted to create a special case
template<int arg>
constexpr int calculate()
{
return arg;
}
template<> int calculate<0>() { return 1; } // special case
template<int arg1, int arg2, int... args>
constexpr int calculate()
{
return calculate<arg1>() + calculate<arg2,args...>();
}
This would make it to where every time you have a zero in you argument list, it will add 1 instead of 0
Another way to do recursion allows you to do it in a single function, utilizing the sizeof... operator:
template<int lhs, int... rhs>
int add()
{
if constexpr(sizeof...(rhs))
{
return lhs + add<rhs...>();
}
else
{
return lhs;
}
}
This also requires C++17 for if constexpr, but it can do things that fold expressions might not be able to.

How do I initialise a std::array field inside a struct with the contents of a variable length sequence container?

I have a struct which in an extremely simplified representation looks like this:
struct Flags {
const std::array<unsigned int, 8> flags;
Flags(std::vector<unsigned int> initialFlagValues) : flags(initialFlagValues) {}
};
Which of course does not compile. For the purposes of my program, I'd like to initialise an arbitrary number of elements in flags, depending on the length of a parameter (std::vector, C-style array, or otherwise) passed into the constructor of the struct.
Now I'd like to use a std::array inside the struct, as the struct itself is created many times (so a std::vector would not be ideal here due to many allocations/deallocations), but the number of values within flags that need to be initialised is not always the same.
Is there a way I can initialise a specific number of fields in flags depending on the size of the sequence container passed as a parameter into the constructor?
Use a helper function, perhaps in the form of a lambda:
Flags(std::vector<unsigned int> initialFlagValues) :
flags([](const auto& init) {
std::array<unsigned int, 8> flags;
// bounds check omitted for brevity
std::copy(init.begin(), init.end(), flags.begin());
return flags;
}(initialFlagValues))
{}
Maybe the helper function can be a delegated constructor
struct Flags
{
std::array<unsigned int, 8u> const flagsArr;
template <std::size_t ... Is>
Flags (std::vector<unsigned int> iFV, std::index_sequence<Is...>)
: flagsArr{ Is < iFV.size() ? iFV[Is] : 0u ... }
{}
Flags (std::vector<unsigned int> iFV)
: Flags{iFV, std::make_index_sequence<8u>{}}
{}
};
You can generalize with for generic types supporting operator [] and std::size() (so also C-style arrays) as follows
struct Flags
{
std::array<unsigned int, 8u> const flagsArr;
template <typename T, std::size_t ... Is>
Flags (T const & iFV, std::index_sequence<Is...>)
: flagsArr{ Is < std::size(iFV) ? iFV[Is] : 0u ... }
{}
template <typename T>
Flags (T const & iFV)
: Flags{iFV, std::make_index_sequence<8u>{}}
{}
};
std::array is an aggregate. That means the only way to initialized it is with a braced_init_list ({}). There is no way to convert a std::vector into a braced_init_list so one thing you can do is to use a loop inside the constructor like
Flags(std::vector<unsigned int> initialFlagValues) : flags{} // zero out flags
{
auto size = std::min(initialFlagValues.size(), flags.size())
for (size_t i = 0; i < size; ++i)
flags[i] = initialFlagValues[i];
}
This will nessecitate that flags be non-const. If that is a change you can't make, then you'll need to make initialFlagValues a std::array, or use a helper function to return an array that you can use to initialize flags with.
You can also do everything at (almost) compile-time, with no iterations:
#include <iostream>
#include <type_traits>
#include <iterator>
struct Omg {
static constexpr std::size_t SIZE = 8;
template<class Container> Omg(Container &&c)
: Omg(std::forward<Container>(c), std::make_index_sequence<SIZE>{})
{}
void omg() const {
for(auto i: array) std::cout << i << ' ';
std::cout << '\n';
}
private:
template<class C, std::size_t... is> Omg(C &&c, std::index_sequence<is...>)
: array{get<is>(std::forward<C>(c))...}
{}
template<std::size_t i, class C> static constexpr auto get(C &&c) {
return i < std::size(c)? c[i] : 0;
}
std::array<int, SIZE> array;
};
int main() {
Omg(std::vector<int>{0, 1, 2, 3}).omg();
int nyan[] = {42, 28, 14};
Omg(nyan).omg();
}

Getting total size in bytes of C++ template parameter pack

I'm trying to create a function that returns the total size of a parameter pack in bytes, and am having trouble finding a solution!
template <typename... TTypes>
struct Test {
constexpr size_t getPackSizeInBytes() const {
// sizeof(TTypes) gives `unexpanded pack` error
}
};
// What I'm hoping for:
Test<uint8, uint16> test;
std::cout << test.getPackSizeInBytes() << std::endl;
// Should output 3;
Thanks for your help!
You can use a unary fold in C++17:
return (sizeof(TTypes) + ... + 0);
If you don't have C++17 then you have to unpack it in a more manual, albeit uglier way:
int sum = 0;
using I = std::size_t[];
(void)(I{0u, sum += sizeof(TTypes)...});
return sum;
Does this do what you want:
// Pre C++17 code (not sure but I belive it is easier in C++17)
#include <iostream>
#include <numeric>
template<typename... TTypes>
struct Test
{
constexpr size_t getPackSizeInBytes() const
{
size_t data[] = {sizeof(TTypes)...};
return std::accumulate(std::begin(data), std::end(data), 0);
}
};
int main()
{
Test<std::uint8_t, std::uint16_t> test;
std::cout << test.getPackSizeInBytes() << "\n";
}
Alternate (much portable, but recursive) solution, based on this answer, not even "Pre C++17", but maybe even "Pre C++11" :) (Works even in Arduino, without any dependencies)
template <typename ... Args>
struct count_bytes;
template <>
struct count_bytes<> {
constexpr static size_t value = 0u;
};
template <typename T, typename... Args>
struct count_bytes<T, Args...> {
constexpr static size_t value = sizeof(T) + count_bytes<Args...>::value;
};
// ----------------------------------------------------------------
// Compile-time testing
static_assert(count_bytes<int8_t, int16_t>::value == 3, "Test failed");
static_assert(count_bytes<int8_t, int16_t, int32_t>::value == 7, "Test failed");
static_assert(count_bytes<float, float, float, double>::value == 20, "Test failed");
// Test for known-size fixed array
static_assert(count_bytes<int, int[2]>::value == 12, "Test failed");
// Attention: sizeof(void) works for C, but not for C++. Reference: https://stackoverflow.com/a/1666232/
// static_assert(count_bytes<void>::value == 1, "Test failed");

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