Related
Good day,
I tried to implement a class which could summary everything with an inner stored variable _val, see below:
#include <utility>
#include <vector>
#include <cassert>
template <typename T>
class Addable {
T _val;
public:
explicit Addable(T v) :_val(std::move(v)) {}
template <typename ...Us>
[[nodiscard]] constexpr T add(Us&& ...us) const
{
return (_val + ... + us);
}
template<typename U>
[[nodiscard]] constexpr T add(U u) const
{
if constexpr (std::is_same_v<T, std::vector<U>>) {
auto copy = _val;
for (auto& n : copy) {
n += u;
}
return copy;
}
else {
return _val + u;
}
}
};
int main()
{
using namespace std;
assert(Addable<int>{42}.add() == 42);
assert(Addable<int>{42}.add(1) == 43);
assert(Addable<int>{42}.add(1, 1) == 44);
assert(Addable<int>{2}.add(1, 1, 1, 1, 1) == 7);
{
vector v {2, 3};
vector expected {3, 4};
assert(Addable<vector<int>>{v}.add(1) == expected);
}
{
vector v {2, 3};
vector expected {5, 6};
// assert(Addable<vector<int>>{v}.add(1, 2) == expected); // compile error...
}
return 0;
}
Class works
- with a fold expression with a simple T like int here.
- with a T like a std::vector but only for one U u.
When I try to append for each vector element one of each from variadic pack its broke, what I did wrong?...
This one overload handles all your test cases:
template <typename ...Us>
[[nodiscard]] constexpr T add(Us&& ...us) const
{
if constexpr (!sizeof...(us))
{
return _val;
}
else if constexpr (std::is_same_v<T, std::vector< std::common_type_t<Us...> > > )
{ // [2]
auto copy = _val;
for (int& i : copy)
i += (us + ...);
return copy;
}
else
{
return (_val + ... + us);
}
}
In [2] block which is invoked for T = vector you need to iterate over all items in copy, and for each one you have to add a result of folding (us + ...) from input arguments.
Live demo
When I try to append for each vector element one of each from variadic pack its broke, what I did wrong?..
I don't understand what do you exactly want... anyway
With
Addable<vector<int>>{v}.add(1, 2)
you call add() with two arguments.
You have two version of add(): the variadic one and the one that receive one argument.
So, calling it with two arguments, only the variadic matches
template <typename ...Us>
[[nodiscard]] constexpr T add(Us&& ...us) const
{
return (_val + ... + us);
}
but the operator + in _val + ... + us, where _val is a std::vector<int> and the us... values are ints, is undefined. So the error.
If you call add() with a single argument, by example
Addable<vector<int>>{v}.add(2)
the code compile (calling the add() version that manages the std::vector case) but, obviously, the assert() fail when you run the compiled program.
I've got a utility called choose_literal which chooses a literal string encoded as char*, wchar_*, char8_t*, char16_t*, char32_t* depending on the desired type (the choice).
It looks like this:
template <typename T>
constexpr auto choose_literal(const char * psz, const wchar_t * wsz, const CHAR8_T * u8z, const char16_t * u16z, const char32_t * u32z) {
if constexpr (std::is_same_v<T, char>)
return psz;
if constexpr (std::is_same_v<T, wchar_t>)
return wsz;
#ifdef char8_t
if constexpr (std::is_same_v<T, char8_t>)
return u8z;
#endif
if constexpr (std::is_same_v<T, char16_t>)
return u16z;
if constexpr (std::is_same_v<T, char32_t>)
return u32z;
}
I supply a little preprocessor macro to make this work w/o having to type each of those string encodings manually:
// generates the appropriate character literal using preprocessor voodoo
// usage: LITERAL(char-type, "literal text")
#define LITERAL(T,x) details::choose_literal<T>(x, L##x, u8##x, u##x, U##x)
This of course only works for literal strings which can be encoded in the target format by the compiler - but something like an empty string can be, as can ASCII characters (i.e. a-z, 0-9, etc., which have representations in all of those encodings).
e.g. here's a trivial bit of code that will return the correct empty-string given a valid character type 'T':
template <typename T>
constexpr const T * GetBlank() {
return LITERAL(T, "");
}
This is great as far as it goes, and it works well enough in my code.
What I'd like to do is to refactor this such that I get back the character-array including its size, as if I'd written something like:
const char blank[] = "";
or
const wchar_t blank[] = L"";
Which allows the compiler to know the length of the string-literal, not just its address.
My choose_literal<T>(str) returns only the const T * rather than the const T (&)[size] which would be ideal.
In general I'd love to be able to pass such entities around intact - rather than have them devolve into just a pointer.
But in this specific case, is there a technique you might point me towards that allows me to declare a struct with a data-member for the desired encoding which then also knows its array-length?
A little bit of constexpr recursion magic allows you to return a string_view of the appropriate type.
#include <string_view>
#include <type_traits>
#include <iostream>
template <typename T, class Choice, std::size_t N, class...Rest>
constexpr auto choose_literal(Choice(& choice)[N], Rest&...rest)
{
using const_char_type = Choice;
using char_type = std::remove_const_t<const_char_type>;
if constexpr (std::is_same_v<T, char_type>)
{
constexpr auto extent = N;
return std::basic_string_view<char_type>(choice, extent - 1);
}
else
{
return choose_literal<T>(rest...);
}
}
int main()
{
auto clit = choose_literal<char>("hello", L"hello");
std::cout << clit;
auto wclit = choose_literal<wchar_t>("hello", L"hello");
std::wcout << wclit;
}
https://godbolt.org/z/4roZ_O
If it were me, I'd probably want to wrap this and other functions into a constexpr class which offers common services like printing the literal in the correct form depending on the stream type, and creating the correct kind of string from the literal.
For example:
#include <string_view>
#include <type_traits>
#include <iostream>
#include <tuple>
template <typename T, class Choice, std::size_t N, class...Rest>
constexpr auto choose_literal(Choice(& choice)[N], Rest&...rest)
{
using const_char_type = Choice;
using char_type = std::remove_const_t<const_char_type>;
if constexpr (std::is_same_v<T, char_type>)
{
constexpr auto extent = N;
return std::basic_string_view<char_type>(choice, extent - 1);
}
else
{
return choose_literal<T>(rest...);
}
}
template<class...Choices>
struct literal_chooser
{
constexpr literal_chooser(Choices&...choices)
: choices_(choices...)
{}
template<class T>
constexpr auto choose()
{
auto invoker = [](auto&...choices)
{
return choose_literal<T>(choices...);
};
return std::apply(invoker, choices_);
}
std::tuple<Choices&...> choices_;
};
template<class Char, class...Choices>
std::basic_ostream<Char>& operator<<(std::basic_ostream<Char>& os, literal_chooser<Choices...> chooser)
{
return os << chooser.template choose<Char>();
}
template<class Char, class...Choices>
std::basic_string<Char> to_string(literal_chooser<Choices...> chooser)
{
auto sview = chooser.template choose<Char>();
return std::basic_string<Char>(sview.data(), sview.size());
}
int main()
{
auto lit = literal_chooser("hello", L"hello");
std::cout << lit << std::endl;
std::wcout << lit << std::endl;
auto s1 = to_string<char>(lit);
auto s2 = to_string<wchar_t>(lit);
std::cout << s1 << std::endl;
std::wcout << s2 << std::endl;
}
The use of the reference argument type Choices& is important. C++ string literals are references to arrays of const Char. Passing by value would result in the literal being decayed into a pointer, which would lose information about the extent of the array.
we can add other services, written in terms of the literal_chooser:
template<class Char, class...Choices>
constexpr std::size_t size(literal_chooser<Choices...> chooser)
{
auto sview = chooser.template choose<Char>();
return sview.size();
}
We're going to change the function so that it takes a const T (&)[size] for each input, and the return type is going to be decltype(auto). Using decltype(auto) prevents the return from decaying into a value, preserving things like references to arrays.
Updated function:
template <typename T, size_t N1, size_t N2, size_t N3, size_t N4>
constexpr decltype(auto) choose_literal(const char (&psz)[N1], const wchar_t (&wsz)[N2], const char16_t (&u16z)[N3], const char32_t (&u32z)[N4]) {
if constexpr (std::is_same<T, char>())
return psz;
if constexpr (std::is_same<T, wchar_t>())
return wsz;
if constexpr (std::is_same<T, char16_t>())
return u16z;
if constexpr (std::is_same<T, char32_t>())
return u32z;
}
In main, we can assign the result to something of type auto&&:
#define LITERAL(T,x) choose_literal<T>(x, L##x, u##x, U##x)
int main() {
constexpr auto&& literal = LITERAL(char, "hello");
return sizeof(literal); // Returns 6
}
Potential simplification
We can simplify the choose_literal function by making it recursive, that way it can be expanded for any number of types. This works without any changes to the LITERAL macro.
template<class T, class Char, size_t N, class... Rest>
constexpr decltype(auto) choose_literal(const Char(&result)[N], Rest const&... rest) {
if constexpr(std::is_same_v<T, Char>)
return result;
else
return choose_literal<T>(rest...);
}
// case 1
const int i = 42;
const auto &k = i;
// case 2
const int i = 42;
auto &k = i;
Do we need the const keyword before auto in this scenario? After all, a reference (k) to an auto-deduced type will include the top level const of the object (const int i). So I believe k will be a reference to an integer that is constant (const int &k) in both cases.
If that is true, does that mean that const auto &k = i; in case 1 is replaced by the compiler as just const int &k = i; (auto being replaced with int)? Whereas in case 2, auto is replaced with const int?
auto keyword automatically decides the type of the variable at compile time.
In your first case, auto is reduced to int where it's reduced to const int in the second case. So, both of your cases are reduced to the same code as:
const int &k = i;
However, it's better to have the const explicitly for better readability and to make sure your variable TRULY is const.
Type deduction with auto works like template argument type deduction with a few exceptions that don't apply in the given example. Hence
const int i = 42;
const auto& k1 = i; // same as const int& k1 = i;
auto& k2 = i; // same as (const int)& k2 = i;
It is probably more readable to add the const qualifier nevertheless.
Here is another example where favoring brevity with auto is misleading:
int *i;
const auto k1 = i; // same as int* const k1 = i;
const auto *k2 = i; // same as const int *k2 = i;
In the first case, the object that i points to can be modified through k1, in the second case, it can't.
Hi and welcome to stack overflow.
As this little test program shows, no matter how you specify the type of k, the compiler will never let you lose the constness of i.
#include <iostream>
#include <type_traits>
#include <string>
#define XSTR(s) STR(s)
#define STR(s) #s
template<class T>
struct type;
template<>
struct type<int>
{
static std::string name() { return "int"; }
};
template<class T>
struct type<T&&>
{
static std::string name() { return type<T>::name() + " &&"; }
};
template<class T>
struct type<T&>
{
static std::string name() { return type<T>::name() + " &"; }
};
template<class T>
struct type<T const>
{
static std::string name() { return type<T>::name() + " const"; }
};
#define REPORT_CONST(decl, var, assign) \
{ \
decl var = assign; \
do_it(STR(decl var = assign;), var); \
}
template<class Var>
void do_it(const char* message, Var&&)
{
std::cout << "case: " << message << " results in type: " << type<Var>::name() << '\n';
}
int main()
{
const int i = 42;
REPORT_CONST(const auto &, k, i);
REPORT_CONST(auto &, k, i);
REPORT_CONST(auto &&, k, std::move(i));
REPORT_CONST(auto const &&, k, std::move(i));
REPORT_CONST(int const&, k, i);
// REPORT_CONST(int &, k, i); // error: binding reference of type 'int&' to 'const int' discards qualifiers
}
Expected results:
case: const auto & k = i; results in type: int const &
case: auto & k = i; results in type: int const &
case: auto && k = std::move(i); results in type: int const &
case: auto const && k = std::move(i); results in type: int const &
case: int const& k = i; results in type: int const &
http://coliru.stacked-crooked.com/a/7c72c8ebcf42c351
Note also the decay of named r-values to l-values.
There is a slight difference in first case auto will be deduced to const int and in the second case to int (as you explcitly stated the const).
cpp-reference states
The keyword auto may be accompanied by modifiers, such as const or &, which will participate in the type deduction. For example, given const auto& i = expr;, the type of i is exactly the type of the argument u in an imaginary template template void f(const U& u) if the function call f(expr) was compiled. Therefore, auto&& may be deduced either as an lvalue reference or rvalue reference according to the initializer, which is used in range-based for loop.
So for you this means
// case 1
const int i = 42;
const auto &k = i; // auto -> int
// case 2
const int i = 42;
auto &k = i; // auto -> const int
More important in my opinion is, however, that the intent is stated more clearly if you state const explcitly and the guarantees the constness. Therefore in this case I would clearly prefer it.
The accepted answer is correct, i.e. there is no difference in regards to the compiled result. What's important to note is that the auto& version is coupling the const-ness of the k reference with the const-ness of the variable i. I figured since the question is titled 'Difference between const auto & and auto & ...' then it's important to emphasize the pragmatic difference here, in which if you don't put a the const keyword, you cannot guarantee the reference will have this cv-qualification. In some scenarios where this is desired, why leave it to chance that i will remain const in the future?
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
I want to have a function that evaluates 2 bool vars (like a truth table).
For example:
Since
T | F : T
then
myfunc('t', 'f', ||); /*defined as: bool myfunc(char lv, char rv, ????)*/
should return true;.
How can I pass the third parameter?
(I know is possible to pass it as a char* but then I will have to have another table to compare operator string and then do the operation which is something I would like to avoid)
Is it possible to pass an operator like ^ (XOR) or || (OR) or && (AND), etc to a function/method?
Declare:
template<class Func> bool myfunc(char lv, char rv, Func func);
Or if you need to link it separately:
bool myfunc(char lv, char rv, std::function<bool(bool,bool)> func);
Then you can call:
myfunc('t', 'f', std::logical_or<bool>());
#ybungalobill posted a C++ correct answer and you should stick to it. If you want to pass the operators, functions will not work, but macros would do the work:
#define MYFUNC(lv, rv, op) ....
// Call it like this
MYFUNC('t', 'f', ||);
Be careful, macros are evil.
What you can do is define proxy operators that return specific types.
namespace detail {
class or {
bool operator()(bool a, bool b) {
return a || b;
}
};
class and {
bool operator()(bool a, bool b) {
return a && b;
}
};
// etc
class X {
or operator||(X x) const { return or(); }
and operator&&(X x) const { return and(); }
};
};
const detail::X boolean;
template<typename T> bool myfunc(bool a, bool b, T t) {
return t(a, b);
}
// and/or
bool myfunc(bool a, bool b, std::function<bool (bool, bool)> func) {
return func(a, b);
}
// example
bool result = myfunc(a, b, boolean || boolean);
You can if desperate chain this effect using templates to pass complex logical expressions.
Also, the XOR operator is bitwise, not logical- although the difference is realistically nothing.
However, there's a reason that lambdas exist in C++0x and it's because this kind of thing flat out sucks in C++03.
In modern C++ can pass any operator by using lambdas.
Update 1: the proposed solution introduces small improvement which is suggested by #HolyBlackCat
#include <iostream>
template<class T, class F> void reveal_or(T a, T b, F f)
{
// using as function(a, b) instead of expression a || b is the same thing
if ( f(a, b) )
std::cout << a << " is || " << b << std::endl;
else
std::cout << a << " is not || " << b << std::endl;
}
template<class T> void reveal_or(T a, T b)
{
// reuse the already defined ||
reveal_or(a, b, [](T t1, T t2) {return t1 || t2; });
}
Don't bother how to pass parameter if || operator is defined
int main ()
{
reveal_or('1', 'a');
return 0;
}
Passing explicitly as parameter. We can pass anything, including including any exotic nonsense
int main ()
{
//same as above:
reveal_or('1', 'a', [](char t1, char t2) { return t1 || t2; });
//opposite of above
reveal_or('1', 'a', [](char t1, char t2) { return !( t1 || t2; ) });
return 0;
}
It's hard to be realized. In C++, function parameter need an memroy address to find its object, but operator is decided in compile time. Operator won't be a object. So you can think about MACRO to finish your task.