C++11 Variadic function in variadic template class not working as expected - c++11

Sincere apologies if this has been answered elsewhere, I did search but couldn't find a clear match.
I have a variadic function in a template class that does not work exactly as I expected. I have a workaround, but I suspect it's not the best solution.
Consider the following code:
#include <iostream>
#include <functional>
#include <vector>
template< typename... ARGS >
class Kitten
{
public:
using Callback = std::function< void( ARGS&&... ) >;
Kitten() = default;
void addCallback( Callback && c )
{
callbacks.push_back( std::forward< Callback >( c ) );
}
void processCallbacks( ARGS &&... args )
{
for ( Callback const &c : callbacks )
{
c( std::forward< ARGS >( args )... );
}
}
private:
std::vector< Callback > callbacks;
};
int main( int argc, const char * argv[] )
{
( void ) argc;
( void ) argv;
Kitten<int, float> kitty;
kitty.addCallback( []( int i, float f )
{
std::cout << "Int: " << i << "\nFloat: " << f << "\n";
} );
kitty.processCallbacks( 2, 3.141f );
int ii = 54;
float ff = 2.13f;
kitty.processCallbacks( ii, ff );
return 0;
}
This will not compile, the second call to processCallbacks will generate an error (clang, similar issue seen on vc14).
I can fix the compilation and get things working as expected if I change the definition of processCallbacks to:
template< typename... FORCEIT >
void processCallbacks( FORCEIT &&... args )
{
for ( Callback const &c : callbacks )
{
c( std::forward< ARGS >( args )... );
}
}
It seems to me to be a bit of a cheesy workaround even if it seems to work, and I suspect I'm missing a better solution.
My understanding of why the first sample fails is because there's no type deduction being done on the argument pack, so the compiler isn't generating the correct code for all cases. The second sample works because it forces the type deduction on the argument pack.
It's been puzzling me for a while on and off. Any help much appreciated.
edit: vc12 compiler error:
error C2664: 'void Kitten<int,float>::processCallbacks(int &&,float &&)' : cannot convert argument 1 from 'int' to 'int &&'
edit: Apple LLVM version 7.0.0 compiler error:
error: rvalue reference to type 'int' cannot bind to lvalue of type 'int'
Regarding the change suggested in the comments to use std::move, addCallback would seem to be even more flexible in the form:
template< typename FUNCTION >
void addCallback( FUNCTION && c )
{
callbacks.emplace_back( std::forward< FUNCTION >( c ) );
}
Using std::forward because the function now takes a universal reference.
As this would allow the following to work:
std::function< void( int, float )> banana( []( int i, float f )
{
std::cout << "Int: " << i << "\nFloat: " << f << "\n";
} );
kitty.addCallback( banana );

void processCallbacks( ARGS &&... args )
{
//...
}
For each type in ARGS the allowed value categories will be set by the template arguments to Kitten. E.g. for Kitten<float, int&, const bool&>, processCallbacks will accept rvalues for the first parameter, lvalues for the second (due to reference collapsing rules) and both for the third (because rvalues can bind to const lvalue references). This will not give you perfect forwarding.
template< typename... FORCEIT >
void processCallbacks( FORCEIT &&... args )
{
//...
}
That function template accepts both lvalues and rvalues because there is type deduction. Such a parameter is known as a forwarding reference parameter. Forwarding reference parameters must be of the form T&&, where T is some deduced template parameter.
If you want to accept lvalues and rvalues and perfect-forward them, you should use the latter form. Although it might seem weird to you at first, this is a fairly common pattern.

Related

Short circuit dispatching and stop condition in a string-literal to type matcher

I am playing with some piece of code, taken from Avoid if-else branching in string to type dispatching answer from Vittorio Romeo, but rewritten to use with C++14 cause Vittorios version uses C++17 fold expressions. I also thought the rewrite would be a good exercise.
Here is the code:
#include <type_traits>
#include <iostream>
#include <utility>
#include <string>
template<char... Cs>
using ct_str = std::integer_sequence<char, Cs...>;
template<typename T, T... Cs>
constexpr ct_str<Cs...> operator""_cs() { return {}; }
template<typename Name, typename T>
struct named_type
{
using name = Name;
using type = T;
};
template<typename... Ts>
struct named_type_list { };
using my_types = named_type_list<
named_type<decltype("int"_cs), int>,
named_type<decltype("bool"_cs), bool>,
named_type<decltype("long"_cs), long>,
named_type<decltype("float"_cs), float>,
named_type<decltype("double"_cs), double>,
named_type<decltype("string"_cs), std::string>
>;
template<std::size_t... Is, char... Cs>
constexpr bool same_impl(const std::string& s,
std::integer_sequence<char, Cs...>,
std::index_sequence<Is...>)
{
const char c_arr[] = {Cs...};
for (std::size_t i = 0; i != sizeof...(Cs); ++i) {
if (s[i] != c_arr[i]) return false;
}
return true;
//Original C++17 (fold expression)
//return ((s[Is] == Cs) && ...);
}
template<char... Cs>
constexpr bool same(const std::string& s, std::integer_sequence<char, Cs...> seq)
{
std::cout << "checking '" << s << "' against '";
std::initializer_list<bool>{ bool(std::cout << Cs)... };
std::cout << "'\n";
return s.size() >= sizeof...(Cs)
&& same_impl(s, seq, std::make_index_sequence<sizeof...(Cs)>{});
}
template<typename... Ts, typename F>
void handle(named_type_list<Ts...>, const std::string& input, F&& f)
{
using expand_type = int[];
expand_type{ 0, (same(input, typename Ts::name{}) && (f(Ts{}), false), 0)... };
//(void)std::initializer_list<int> {
// ( (same(input, typename Ts::name{}) && (f(Ts{}), false) ), 0)...
//};
//Original C++17 (fold expression)
//( (same(input, typename Ts::name{}) && (f(Ts{}), true) ) || ...);
}
int main(int argc, char** argv)
{
const std::string input{"float"};
handle(my_types{}, input, [](auto t)
{
std::cout << typeid(typename decltype(t)::type).name() << "\n";
// TEST: define std::vector with value_type (matched type "float") and add a few values
using mtype = typename decltype(t)::type;
std::vector<mtype> x;
x.push_back(2.2); // <-- does not compile
});
return 0;
}
I assume problem lies in the handle function that seems not to stop the evaluation properly. It should stop at the first invocation of f() in case of a match. Instead, it executes f() in case of a match as expected, but continues executing the remaining types in the named_type_list.
The current code results in this output:
checking 'float' against 'int'
checking 'float' against 'bool'
checking 'float' against 'long'
checking 'float' against 'float'
f
checking 'float' against 'double'
checking 'float' against 'string'
Actually I have no clue how to get that fixed. I tried to rewrite the C++17 fold expression using the std::initializer_list trick and also tried to use an expander (the uncommented part in the handle body. So I guess it is the expression itself not working properly.
Unfortunately I am out of ideas whats really happening at this point, also the fact that I am not experienced with Meta-Programming/Compile-time evaluation.
Another problem arises with an possible use of this code:
My use case would be in an XML property reader where I have type/value tags, e.g. <attribute type="double" value="2.5"/>, applying something like the handle function to get the typename from the type attribute value. That type I could use to further process the value.
For this I added within the handle f()-body in main() 3 lines, defining an std::vector with the found type and trying to add a value to it. This code does not compile, g++ responds with
error: no matching function for call to ‘std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > >::push_back(double)’
I guess this is the mixup out of compile-time and run-time behaviour and it does not work this way and that makes me curious how I could further process/use the matched type.
Thanks for your time on explanation, any help is greatly appreciated!
||, of course, short-circuits. Your version doesn't. I don't see short-circuiting as essential to correctness here, but if you want, it's easily implemented with an additional bool:
bool found = false;
expand_type{ 0, (!found &&
same(input, typename Ts::name{}) &&
(f(Ts{}), found = true), 0)... };
The second problem is because your handler function must be validly callable for every possible type in the type list, but you can't push_back 2.2 into a std::vector<std::string>. As an example, you might have something that obtains the value as a string, and the handler body could lexical_cast it to mtype.

C++11: Variadic template deduction logic

I have the following construct:
template <class... Args>
class some_class
{
public:
some_class() = default;
some_class(Args...) = delete;
~some_class() = default;
};
template<>
class some_class<void>
{
public:
some_class() = default;
~some_class() = default;
};
The reason for this is that I just want to allow the users to create objects using the default constructor, so for example:
some_class<int,float> b;
should work but
some_class<int,float> c(1,3.4);
should give me a compilation error.
At some point in time I also needed to create templates based on void hence, the specialization for void:
some_class<void> a;
But by mistake I have typed:
some_class<> d;
And suddenly my code stopped compiling and it gave me the error:
some_class<Args>::some_class(Args ...) [with Args = {}]’ cannot be
overloaded
some_class(Args...) = delete;
So here comes the question: I feel that I am wrong that I assume that some_class<> should be deduced to the void specialization... I just don't know why. Can please someone explain why some_class<> (ie: empty argument list) is different from some_class<void>? (A few lines from the standard will do :) )
https://ideone.com/o6u0D6
void is a type like any other (an incomplete type, to be precise). This means it can be used as a template argument for type template parameters normally. Taking your class template, these are all perfectly valid, and distinct, instantiations:
some_class<void>
some_class<void, void>
some_class<void, void, void>
some_class<void, char, void>
In the first case, the parameter pack Args has one element: void. In the second case, it has two elements: void and void. And so on.
This is quite different from the case some_class<>, in which case the parameter pack has zero elements. You can easily demonstrate this using sizeof...:
template <class... Pack>
struct Sizer
{
static constexpr size_t size = sizeof...(Pack);
};
int main()
{
std::cout << Sizer<>::size << ' ' << Sizer<void>::size << ' ' << Sizer<void, void>::size << std::endl;
}
This will output:
0 1 2
[Live example]
I can't really think of a relevant part of the standard to quote. Perhaps this (C++11 [temp.variadic] 14.5.3/1):
A template parameter pack is a template parameter that accepts zero or more template arguments. [ Example:
template<class ... Types> struct Tuple { };
Tuple<> t0; // Types contains no arguments
Tuple<int> t1; // Types contains one argument: int
Tuple<int, float> t2; // Types contains two arguments: int and float
Tuple<0> error; // error: 0 is not a type
—end example ]

Different behavior of Visual Studio and gcc when compiling variadic templates program

I'm playing with C++11's variadic templates, and here is my code:
#include "iostream"
void func(){
std::cout << std::endl;
}
template< typename ...Params> void func(int x, Params... params){
std::cout << " int " << x;
func(params...);
}
template< typename ...Params> void func(float x, Params... params){
std::cout << " float " << x;
func(params...);
}
template< typename ...Params> void func(const char* x, Params... params){
std::cout << " const char* " << x;
func(params...);
}
int main(int argc, char* argv[])
{
func(3.14f, 5, "Test");
getchar();
return 0;
}
The code above can be compiled and run on Visual Studio 2013 without any problem, but if I compile it with gcc 4.6.3 (I don't have gcc environment and using online service like repl.it), this code will produce error like this:
main.cpp: In instantiation of 'void func(int, Params ...) [with Params = {const char*}]':
main.cpp:15:6: required from 'void func(float, Params ...) [with Params = {int, const char*}]'
main.cpp:24:23: required from here
main.cpp:11:6: error: no matching function for call to 'func(const char*&)'
func(params...);
~~~~^~~~~~~~~~~
main.cpp:5:6: note: candidate: void func()
void func(){
^~~~
main.cpp:5:6: note: candidate expects 0 arguments, 1 provided
main.cpp:9:36: note: candidate: template<class ... Params> void func(int, Params ...)
template< typename ...Params> void func(int x, Params... params){
^~~~
main.cpp:9:36: note: template argument deduction/substitution failed:
main.cpp:11:6: note: cannot convert 'params#0' (type 'const char*') to type 'int'
func(params...);
~~~~^~~~~~~~~~~
exit status 1
Whose behavior is right? Is this code has some problem I don't acknowledge or this is a bug of gcc?
BTW: If I change func(3.14f,5,"test") into func(3.14f,5) then gcc can compile this code as well.
gcc behaviour is right.
Roughly speaking, when you instantiate a template, the instantiation needs to be legal C++ code when placed where the original template definition was found. (More precisely, all names except so-called dependent names need to be resolvable at the point of template definition).
In your case, the first template instantiates to an equivalent of
void func(int x, const char *params0){
std::cout << " int " << x;
func(params0);
}
and the last call is not valid at the point of template definition. Indeed, the template that can handle the call does not get declared until later in the code.
VC++ is known for its non-compliant handling of templates: it only checks validity of the instantiation at the point of instantiation, not at the point of the original template definition. This code illustrates it nicely.
To bring your code to compliance, first forward-declare the templates and then define them.

C++11 Check two sets of variadic template arguments match

This question relates to an earlier one I asked regarding implementing something akin to Qt's signal/slots in C++11.
Consider the following (very simplified signal dispatcher, that in this example does nothing of any use, it's just to demonstrate the pattern/problem):
template< typename... TYPES >
class Signal
{
public:
Signal() = default;
~Signal() = default;
template< typename... PARAMETERS >
void broadcast( PARAMETERS &&... p )
{
// static_assert to confirm PARAMETERS can map to TYPES
}
};
This works well enough, but there's some unwanted type conversion going on in practice. e.g.;
// acceptable use.
Signal< int, unsigned, float, char >().broadcast( 1, 2u, 0.f, 'a' );
// should fail compilation, first parameter is a float, 4th is an int.
Signal< int, unsigned, float, char >().broadcast( 0.f, 0, 0.f, 0 );
// acceptable use, first parameter is const, but it's convertible.
const int i = 3;
Signal< int, unsigned, float, char >().broadcast( i, 2u, 0.f, 'a');
// acceptable use, first parameter is const &, but it's convertible.
const int & j = i;
Signal< int, unsigned, float, char >().broadcast( j, 2u, 0.f, 'a');
There should be no silent float to int conversion. Conversion of const/const & in this instance should be possible (the format of TYPES should not have const or & as all data should be passed by value).
I'd like to prevent compilation where such unwanted type conversion happens. I thought to wrap up both TYPES and PARAMETERS in tuples, iterate over the tuple and confirm that each type in a given tuple parameter index matches (including using std::decay), but then I couldn't see a way to do that at compile time so that it could go in a static_assert.
For reference, compilers of choice are clang (latest on OS X 7.3 (clang-703.0.31)) and vc14.
Is what I want to do possible and, if so, can anyone offer any pointers?
Using (once again) the all_true bool pack trick from Columbo:
template <bool...> struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;
template <class... Args>
struct Signal {
template <class... Dargs, class = typename std::enable_if<all_true<
std::is_same<Args, typename std::decay<Dargs>::type>{}...
>{}>::type>
void broadcast(Dargs &&...) {}
};
This SFINAE's away the function if the parameters don't match exactly.
Here is a metaprogram I quickly came up with. It is a bit coarse, but can be implemented in a more better way. You should probably use the decayed type (std::decay) in the metaprogram to get correct result.
#include <iostream>
#include <type_traits>
template <typename... T> struct param_pack {};
template <typename, typename> struct is_all_same_impl;
template <>
struct is_all_same_impl<param_pack<>, param_pack<>>
{
static bool const value = true;
};
template <typename T, typename S, typename... Rest, typename... SRest>
struct is_all_same_impl<param_pack<T, Rest...>, param_pack<S, SRest...>>
{
static bool const value = false;
};
template <typename T, typename... Rest, typename... SRest>
struct is_all_same_impl<param_pack<T, Rest...>, param_pack<T, SRest...>>
{
static bool const value = is_all_same_impl<param_pack<Rest...>, param_pack<SRest...>>::value;
};
template <typename, typename>
struct is_all_same;
template <typename... FSet, typename... SSet>
struct is_all_same<param_pack<FSet...>, param_pack<SSet...>>: is_all_same_impl<param_pack<FSet...>, param_pack<SSet...>> {};
int main() {
std::cout << is_all_same<param_pack<int, char, float>, param_pack<int, char, int>>::value << std::endl;
return 0;
}
UPDATE :: More simpler version
template <typename... T> struct param_pack {};
int main() {
std::cout << std::is_same<param_pack<int, float, int>, param_pack<int,float,int>>::value << std::endl;
return 0;
}
So you can do something like:
static_assert( is_same<param_pack<Args...>, param_pack<std::decay_t<Dargs>...>>::value, "Parameters do not sufficiently match." );

Why reference_wrapper behaves differently for built-in types?

I have the following use of std::reference_wrapper for a build in type (double) and for a user defined type (std::string).
Why do they behave differently in the case of the stream operator?
#include<functional> //reference wrapper
#include<iostream>
void fd(double& d){}
void fs(std::string& s){}
int main(){
double D = 5.;
std::reference_wrapper<double> DR(D);
std::cout << "DR = " << DR << std::endl; //ok
fd(DR); // ok
std::string S = "hello";
std::reference_wrapper<std::string> SR(S);
std::cout << "SR = " << static_cast<std::string&>(SR) << std::endl; // ok
std::cout << "SR = " << SR << std::endl; // error: invalid operands to binary expression ('basic_ostream<char, std::char_traits<char> >' and 'std::reference_wrapper<std::string>')
fs(SR); // ok
}
http://coliru.stacked-crooked.com/a/fc4c614d6b7da690
Why in the first case DR is converted to double and printed and in the second it is not? Is there a work around?
Ok, I see now, in the ostream case I was trying to called a templated function that is not resolved:
#include<functional> //reference wrapper
void double_fun(double const& t){};
template<class C>
void string_fun(std::basic_string<C> const& t){};
int main(){
double D = 5.;
std::reference_wrapper<double> DR(D);
double_fun(DR); //ok
std::string S = "hello";
std::reference_wrapper<std::string> SR(S);
string_fun(SR); // error: no matching function for call to 'string_fun'
string_fun(SR.get()); // ok
string_fun(static_cast<std::string&>(SR)); // ok
string_fun(*&SR); // would be ok if `std::reference_wrapper` was designed/coded differently, see http://stackoverflow.com/a/34144470/225186
}
For the first part TC gave you the answer. That is, operator<< for basic_string is templated, and template argument deduction doesn't look through implicit conversions.
You could alternatively call SR.get() if you don't want to explicitly to static_cast your reference wrapper.
Now for the second part, string_fun takes as input arguments std::basic_string<C> objects. When you call:
string_fun(SR);
with SR as input parameter which is of type std::reference_wrapper<std::string>, naturally you get a type mismatch.
What you can do is provide an additional overload:
template<class C>
void string_fun(std::reference_wrapper<std::basic_string<C>> const& t) {
};
Live Demo
Or if you want a more unified treatment you could define your string_fun to take template template arguments, and resolve the type with some kind of type trait magic like bellow:
template<template<typename...> class C, typename T>
void
string_fun(C<T> const &t) {
std::cout <<
static_cast<std::conditional_t<
std::is_same<
std::reference_wrapper<T>, C<T>>::value, T, std::basic_string<T>>>(t) << std::endl;
}
Live Demo

Resources