This has been asked before, but I'm curious to see if anything has changed in newer C++ standards. Any current or future standard is acceptable.
Q: Is there anyway to create an Array class that can be initialized with a braced-init-list without having to manually specify the array length, with elements stored on the stack, and doesn't require a 'make_array' function.
template<class T, size_t N>
struct Array
{
T items[N];
};
Array<int> foo = { 1, 2, 3 };
Since initializer_list is not templated on the size, a constructor using it won't do the job. Deduction guides in C++17 nearly work, but you have to omit the type parameter and all items must have exactly the same type
Array foo = { 1, 2, 3 }; // Works
Array<int> foo = { 1, 2, 3 }; // Doesn't work
Array foo = { 1.0, 2.0, 3.0f }; //Doesn't work
A constructor that takes a c-array doesn't appear to work because an initializer_list won't convert to a c-array.
Is the braced-init-list to T[N] that happens in int foo[] = { 1, 2, 3 }; purely compiler magic that can't be replicated in code?
EDIT: The spirit of this question is about the exact syntax above. No make_array, no extra template argument, explicit item type, no double braces, no dynamic allocations. If a trivial Array requires a bunch of modern C++ tomfoolery and still can't manage to support standard syntax then it's just a bad engineering trade-off in my opinion.
You can work around the need for all the types in the list to be the same by using an explicit deduction guide:
template <class... T>
Array(T&&... t) -> Array<std::common_type_t<T...>, sizeof...(T)>;
Array foo = { 1.0, 2.0, 3.0f }; // Deduces Array<double,3u>
I hate to be the bearer of bad news, but I believe that at the current time (at least as of C++17) the answer to your question is "no" there is no way that would meet all of your given requirements. That said, there are solutions posted here that come close, but they all fall short in one or more of your requirements.
I also suspect that the answer will remain "no" in the near future. Not that I'm a prophet, but the direction of the last few C++ versions would seem to imply that a make_array solution is more likely to be what is added rather than more direct language support.
Explanation
Allow me to explain why in a little more detail.
First consider the C++17 deduction guides. I won't go into detail on them as they are discussed suitably by other answers on this question. They do come very close to what you are asking but do seem to fall short in one way or the other. (Although the answer by #max66 would seem to meet all your requirements except for the extra brackets. If his syntax in fact works you may want to consider that answer as "close enough".)
Next consider a variatic templates solution. In order to automatically determine N you would need a series of overloaded functions (basically one with a single argument and one that takes a single argument and the rest of the variatic templates). But that would essentially be equivalent to some form of make_array so it doesn't count either.
Finally, the only other option I could see would be based on an initializer_list. The question is how to determine N from that list. In C++11 this would be obviously impossible since the only access to the size of the list is const and not constexpr. However as of C++14 the size() method is in fact constexpr so you would think it is at least theoretically possible to get the compiler to deduce N based on that. Unfortunately to do this you would need to make N (a template parameter) default to a value of something in the class constructor (the initializer list). I have been unable to determine any means of doing this in the present form of the language.
How I think a future version could support this
One way to support this would be to follow the examples of other languages and add direct language support, bridging a syntax to certain classes. But this essentially makes some classes "special". Consider the following line in Swift:
let ar = [1, 2, 3, 4]
In this example ar is an object of the type Array<Int>. But this is done by direct compiler support, i.e. "Array" is a special case. No matter what you do you could not write a MyArray class that would perform the same way (other than perhaps having MyArray accept an Array as a construction option). It is certainly possible for the C++ standard to be extended to do something similar, but C++ tends to try to avoid those "special" cases. Besides, an argument could be made that
auto ar = make_array(1, 2, 3, 4);
is in fact a clearer representation of the intent than would be something like the following: (suggesting the character 'A' to distinguish this from an initializer list)
auto ar = A{ 1, 2, 3, 4 };
Another way, more in line with current C++ syntax, to do this would be to add the N parameter to the initializer_list template class. After all since size() is now constexpr the size must be known at compile time, so why not make it available as a template parameter? It could suitably default so that it could rarely be needed, but it would allow classes (both standard ones like std::array and custom ones like you are proposing) the possibility of tying the N in the Array template to the N in the initializer_list. Then you should be able to write something along the following lines:
template<class T, size_t N>
struct Array
{
explicit Array(std::initializer_list<N> il);
}
Of course the trick would be to make this initializer_list change in a fashion that does not break a lot of existing code.
I suspect the standards committee will not follow either of these paths, but will more likely add the experimental make_array method. And I'm not convinced that is a bad idea. We are used to make_... in many other parts of the language, so why not here as well?
Here's one approach that allows you to specify the type. We use an extra argument to specify the type, so that we can still use the deduction guide to pick up the size. I don't consider it pretty though:
#include <iostream>
template <typename T>
struct Tag { };
template <typename T, size_t N>
struct Array {
T data_[N];
template <typename... U>
Array(Tag<T>, U... u)
: data_{static_cast<T>(u)...} // cast to shut up narrowing conversions - bad idea??
{}
};
template <typename T, typename... U>
Array(Tag<T>, U...) -> Array<T, sizeof...(U)>;
int main()
{
Array a{Tag<double>{}, 1, 2.0f, 3.0};
for (auto d : a.data_) {
std::cout << d << '\n';
}
}
This is clearly not a full implementation of such a class, just to illustrate the technique.
Deduction guides in C++17 nearly work, but you have to omit the type parameter and all items must have exactly the same type
Not necessarily.
Yes, you can't explicit the type parameter, but you can decide it according the types of the items.
I imagine two reasonable strategies: (1) the type of the Array is the type of the first item, following the std::array way, so writing the following deduction guide
template <typename T, typename ... Us>
Array(T, Us...) -> Array<T, 1u + sizeof...(Us)>;
(but observe that a C++ program where a Us type is different from T, for a std::array, is ill formed) or (2) follows the metalfox's suggestion and select the item's common type
template <typename ... Ts>
Array(Ts...) -> Array<std::common_type_t<Ts...>, sizeof...(Ts)>;
Is the braced-init-list to T[N] that happens in int foo[] = { 1, 2, 3 }; purely compiler magic that can't be replicated in code?
Are you thinking in a deduction guide as follows ?
template <typename T, std::size_t N>
Array(T const (&)[N]) -> Array<T, N>;
Works but with a couple of drawbacks: (1) you have to add a couple of brackets using it
// added ---V V--- added
Array foo1 = { { 1, 2, 3 } }; // Works
and (2) remain the problem that all items must have the same type
Array foo2 = { {1.0, 2.0, 3.0f} }; //Doesn't work: incompatible types
or the compiler can't deduce the type T
P.s.: what's wrong with a make_array() function ?
P.s.2: I suggest to give a look at BoBTFish's answer to see a nice method to bypass the impossibility to explicit a template argument using deduction guides.
You had asked this:
Q: Is there anyway to create an Array class that can be initialized with a braced-init-list without having to manually specify the array length, and without a 'make_array' function.
I have worked on an implementation of a class that behaves in the manner you are describing only it has a little bit more complexity to it but still fairly simple, readable, reusable, portable and generic.
I was not able to have a T items[] array as a direct member in the class. I had to use a T* items instead and create an overloaded operator[] in the derived class to mimic the behavior of an array. This doesn't mean that there isn't any work around to this as others have shown. I just find that this is one possible solution without having to specify the size of the array.
I use a base class to store the elements from the constructor either by means of an std::initializer_list or of a variadic constructor. The class templates themselves are not a variadic template only their constructor's are. The base class stores the values from the initializer_list or parameter pack into an std::vector. The inherited class stores the contents from the vector into T* by calling the data() function of the vector class.
template<typename T>
class ParamPack {
protected:
std::vector<T> values_;
size_t size_;
public:
template<typename... U>
ParamPack( U... u ) :
values_{ static_cast<T>(u)... },
size_( sizeof...(U) ) {}
template<typename ... U>
ParamPack( std::initializer_list<std::is_same<T, U...>( U...)> il ) :
values_( il ), size_( il.size() ) {}
std::vector<T>& operator()() { return values_; }
size_t size() const { return size_; }
};
template<typename T>
class Array : public ParamPack<T> {
private:
T* items_;
public:
template<typename... U>
Array( U... u ) : ParamPack<T>::ParamPack( u... ) {
items_ = this->values_.data();
}
template<typename... U>
Array( std::initializer_list<U...> il ) : ParamPack<T>::ParamPack( il ) {
items_ = this->values_.data();
}
T& operator[]( size_t idx ) {
return items_[idx];
}
T operator[]( size_t idx ) const {
return items_[idx];
}
T* data() const { return items_; }
};
int main() {
try {
// Parameter Pack Examples:
// Variadic Constructor { ... }
std::cout << "ParamPack<T> Examples:\n";
std::cout << "Using ParamPack<T>'s Variadic Constructor\n";
ParamPack<int> pp1( 1, 2, 3, 4 );
std::cout << "Size: " << pp1.size() << " | Elements: ";
for( auto& v : pp1() ) {
std::cout << v << " ";
}
std::cout << '\n';
std::cout << "Using ParamPack<T>'s Variadic Constructor with an Initializer List\n";
ParamPack<int> pp2( { 5, 6, 7, 8 } );
std::cout << "Size: " << pp2.size() << " | Elements: ";
for( auto& v : pp2() ) {
std::cout << v << " ";
}
std::cout << '\n';
std::cout << "Using ParamPack<T>'s initializer_list constructor\n";
std::initializer_list<int> il{ 9,10,11,12 };
ParamPack<int> pp3( il );
std::cout << "Size: " << pp3.size() << " | Elements: ";
for( auto& v : pp3() ) {
std::cout << v << " ";
}
std::cout << "\n\n";
// Array Examples:
std::cout << "Array<T> Examples:\n";
std::cout << "Using Array<T>'s initializer_list Constructor\n";
Array<int> arr( il );
for( size_t i = 0; i < arr.size(); i++ ) {
std::cout << arr[i] << " ";
}
std::cout << "\n";
// Using Variadic Constructor
std::cout << "Using Array<T>'s Variadic Constructor\n";
Array<int> testA( 9, 8, 7, 6 );
for( size_t i = 0; i < testA.size(); i++ ) {
std::cout << testA[i] << " ";
}
std::cout << '\n';
Array<std::string> testB( "Hello", " World" );
for( size_t i = 0; i < testB.size(); i++ ) {
std::cout << testB[i] << " ";
}
std::cout << "\n\n";
// Using Constructor w/ Initializer List
std::cout << "Using Array<T>'s Variadic Constructor with Initializer List\n";
Array<int> testC( { 105, 210, 420 } );
for( size_t i = 0; i < testC.size(); i++ ) {
std::cout << testC[i] << " ";
}
std::cout << "\n\n";
// Using Initializer List with =
std::cout << "Using Array<T>'s Initializer List with =\n";
Array<int> a = { 1, 2, 3, 4 };
for( size_t i = 0; i < a.size(); i++ ) {
std::cout << a[i] << " ";
}
std::cout << '\n';
Array<char> b = { 'a', 'b', 'c', 'd' };
for ( size_t i = 0; i < b.size(); i++ ) {
std::cout << b[i] << " ";
}
std::cout << '\n';
Array<double> c = { 1.2, 3.4, 4.5, 6.7 };
for( size_t i = 0; i < c.size(); i++ ) {
std::cout << c[i] << " ";
}
std::cout << "\n\n";
// Using Initializer List directly
std::cout << "Using Array<T>'s Initalizer List directly\n";
Array<uint32_t> a1{ 3, 6, 9, 12 };
for( size_t i = 0; i < a1.size(); i++ ) {
std::cout << a1[i] << " ";
}
std::cout << "\n\n";
// Using user defined data type
struct Point {
int x_, y_;
Point( int x, int y ) : x_( x ), y_( y ) {}
};
Point p1( 1, 2 ), p2( 3, 4 ), p3( 5, 6 );
// Variadic Constructor
std::cout << "Using Array<T>'s Variadic Consturctor with user data type\n";
Array<Point> d1( p1, p2, p3 );
for( size_t i = 0; i < d1.size(); i++ ) {
std::cout << "(" << d1[i].x_ << "," << d1[i].y_ << ") ";
}
std::cout << '\n';
// Initializer List Construtor (reversed order)
std::cout << "Using Array<T>'s Initializer List Constructor with user data type\n";
Array<Point> d2( { p3, p2, p1 } );
for( size_t i = 0; i < d2.size(); i++ ) {
std::cout << "(" << d2[i].x_ << "," << d2[i].y_ << ") ";
}
std::cout << '\n';
// Initializer List Version = {...} p2 first
std::cout << "Using Array<T>'s = Initializer List with user data type\n";
Array<Point> d3 = { p2, p1, p3 };
for( size_t i = 0; i < d3.size(); i++ ) {
std::cout << "(" << d3[i].x_ << "," << d3[i].y_ << ") ";
}
std::cout << '\n';
// Initializer List Directly p2 first p1 & p3 swapped
std::cout << "Using Array<T>'s Initializer List directly with user data type\n";
Array<Point> d4{ p2, p3, p1 };
for( size_t i = 0; i < d4.size(); i++ ) {
std::cout << "(" << d4[i].x_ << "," << d4[i].y_ << ") ";
}
std::cout << '\n';
std::initializer_list<Point> ilPoints{ p1, p2, p3 };
std::cout << "Using Array<T>'s initializer_list Constructor with user data type\n";
Array<Point> d5( ilPoints );
for( size_t i = 0; i < d5.size(); i++ ) {
std::cout << "(" << d5[i].x_ << "," << d5[i].y_ << ") ";
}
std::cout << "\n\n";
// Need a local copy of the vector instead?
std::cout << "Using Array<T>'s base class's operator()() to retrieve vector\n";
std::vector<Point> points = d4(); // using operator()()
for( auto& p : points ) {
std::cout << "(" << p.x_ << "," << p.y_ << ") ";
}
std::cout << '\n';
// Need a local copy of the pointer instead?
std::cout << "Using Array<T>'s data() to get the contents of its internal pointer\n";
Point* pPoint = nullptr;
pPoint = d4.data();
for( size_t i = 0; i < d4.size(); i++ ) {
std::cout << "(" << pPoint[i].x_ << "," << pPoint[i].y_ << ") ";
}
std::cout << '\n';
} catch( const std::runtime_error& e ) {
std::cerr << e.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
-Output-
ParamPack<T> Examples:
Using ParamPack<T>'s Variadic Constructor
Size: 4 | Elements: 1 2 3 4
Using ParamPack<T>'s Variadic Constructor with an Initializer List
Size: 4 | Elements: 5 6 7 8
Using ParamPack<T>'s initializer_list Constructor
Size: 4 | Elements: 9 10 11 12
Array<T> Examples:
Using Array<T>'s initializer_list Constructor
9 10 12 12
Using Array<T>'s Variadic Constructor
9 8 7 6
Hello World
Using Array<T>'s Constructor with Initializer List
105 210 420
Using Array<T>'s Initializer List with =
1 2 3 4
a b c d
1.2 3.4 5.6 7.8
Using Array<T>'s Initializer List directly
3 6 9 12
Using Array<T>'s Variadic Constructor with user data type
(1,2) (3,4) (5,6)
Using Array<T>'s Variadic Constructor With Initializer List of user data type
(5,6) (3,4) (1,2)
Using Array<T>'s = Initializer List with user data type
(3,4) (1,2) (5,6)
Using Array<T>'s Initializer List directly with user data type
(3,4) (5,6) (1,2)
Using Array<T>'s initializer_list Constructor with user data type
Using Array<T>'s base class's operator()() to retrieve vector
(3,4) (5,6) (1,2)
Using Array<T>'s data() to get the contents of its internal pointer
(3,4) (5,6) (1,2)
Now this is a little more robust as it has operators that are available from both the parent and child class, from the parent class you can get the stored vector directly from its operator()(). From the child class you can index into the child's stored pointer from operator[]() and there is a function to return it's size. The template itself does not contain a size_t N template argument since the size is stored internally in the base class and is determined by the size of its vector. With this I'm treating T* p as if it was T p[size]. This class is still not without some limitations.
-Valid Construction-
Array<int> a( 1, 2, 3, 4 ); // Variadic Constructor Okay
Array<int> a( {1,2,3,4} ); // Initializer List Constructor Okay
Array<int> a = { 1, 2, 3, 4 }; // Initializer List Okay
Array<int> a{ 1,2,3,4 }; // Initializer List Okay
-Limitations-
However you have to explicitly instantiate the template as these will not work as they all generate a compiler error.
Array a( 1,2,3,4 );
Array a( {1,2,3,4} );
Array a = { 1, 2, 3, 4 };
Array a{ 1,2,3,4 };
-Note- There maybe more that can be done to make this more efficient, maybe even thread safe or exception safe, but this is just a generalization of the class design.
Please let me know what you think about this:
Is there any way at all for this code to compile and work as intended without resorting to va_list stuff ?
#include <iostream>
void fct(void)
{
std::cout << std::endl;
}
void fct(int index, int indexes...)
{
std::cout << index << ' ';
fct(indexes); //or fct(indexes...); ?
}
int main(void)
{
fct(1, 2, 3, 4, 5, 6, 7);
return 0;
}
I suspect you have misunderstood the meaning of the signature
void fct (int index, int indexes...)
I suspect you think that fct() expect a int single value (index) and a variadic list of int's (indexex...) with C++11 style of parameter pack expansion.
No: it's the same as
void fct (int index, int indexes, ...)
so two int single values and a C-style of optional argument that you can use only through va_list stuff.
If you don't believe it, try calling fct() with only an integer argument
fct(1);
You should obtain an error of type "error: no matching function for call to 'fct'" with a note of type "note: candidate function not viable: requires at least 2 arguments, but 1 was provided" regarding the variadic version of fct().
If you want receive a variadic list of parameters and recursively pass the to the same function, you can use the template variadic way.
By example
template <typename ... Ts>
void fct(int index, Ts ... indexes)
{
std::cout << index << ' ';
fct(indexes...);
}
If you really dislike the idea of a template, I guess you could cheat a bit like this:
#include <iostream>
#include <vector>
void fct(std::vector<int>&& _indices)
{
for (auto&& i : _indices)
{
std::cout << i << ' ';
}
std::cout << std::endl;
}
int main(void)
{
fct({1, 2, 3, 4, 5, 6, 7}); // Note the curly braces
return 0;
}
I am using C++03. Thus I don't have std::bind(). My compiler is ported from g++, but tr1::bind() is not ported.
I want to bind a function having 3 arguments. Thus std::bind1st() and std::bind2nd() won't work. In addition, neither std::bind1st() or std::bind2nd() deal with reference type of argument.
Thus I am writing my own version of bind1st() as follows, including sample invocation of my bind1st():
#include <iostream>
// c++03 does not define nullptr
#ifndef nullptr
#define nullptr ((void*)0)
#endif
namespace mybinder {
void *binder3ArgToBind;
void *binder3FunctorToCall;
template <int uniqueID, typename USER_FUNC_TYPE, typename BOUND_FUNC_TYPE,
typename returnType, typename T1, typename T2, typename T3>
class binder3 // 3 arguments binder
{
public:
static void setParams(void **pArg1, void **pFunc) {
*pArg1 = binder3ArgToBind;
*pFunc = binder3FunctorToCall;
binder3ArgToBind = nullptr;
}
static returnType f_bound1st_common(T2 &arg2, T3 &arg3) {
static void *arg1;
static void *func;
returnType ignored;
if (binder3ArgToBind != nullptr) {
setParams(&arg1, &func);
return ignored;
}
return ((USER_FUNC_TYPE)func)(*(T1*)arg1, arg2, arg3);
}
static returnType f_bound1st(T2 arg2, T3 arg3) {
return f_bound1st_common(arg2, arg3);
}
static returnType f_bound1st(T2 &arg2, T3 arg3) {
return f_bound1st_common(arg2, arg3);
}
static returnType f_bound1st(T2 arg2, T3 &arg3) {
return f_bound1st_common(arg2, arg3);
}
static returnType f_bound1st(T2 &arg2, T3 &arg3) {
return f_bound1st_common(arg2, arg3);
}
static BOUND_FUNC_TYPE bind1st(USER_FUNC_TYPE f, T1 &arg1) {
binder3FunctorToCall = (void*)f;
binder3ArgToBind = (void*) &arg1;
(*(void (*)())f_bound1st_common)();
return f_bound1st;
}
};
}
using namespace std;
using namespace mybinder;
typedef short (*FUNC)(int const, int&);
// test function
short f(int &a, int const b, int &c)
{
int val = 100 * a + 10 * b + c;
a+=100;
c+=10;
return val;
}
int main(void)
{
int a[2] = {2, 5};
for (int i=0; i<2; ++i) {
// bind function f with a[i] as its 1st argument.
FUNC ff = binder3<__COUNTER__, short(*)(int&, int const, int&), short(*)(int const,int&), short, int, int const, int>::bind1st(f, a[i]);
int k;
int m;
for (m=0; m<2; ++m) {
k = 1-m;
cout << "before: a[i]=" << a[i] << endl;
cout << "before: m=" << m << endl;
cout << "before: k=" << k << endl;
cout << ff(m, k) << endl;
cout << "after: a[i]=" << a[i] << endl;
cout << "after: m=" << m << endl;
cout << "after: k=" << k << endl << endl;
}
}
}
And the execution results:
before: a[i]=2
before: m=0
before: k=1
201
after: a[i]=102
after: m=0
after: k=11
before: a[i]=102
before: m=1
before: k=0
10210
after: a[i]=202
after: m=1
after: k=10
before: a[i]=5
before: m=0
before: k=1
501
after: a[i]=105
after: m=0
after: k=11
before: a[i]=105
before: m=1
before: k=0
10510
after: a[i]=205
after: m=1
after: k=10
It works fine on my PC, as an emulation. However, it raises several problems in my embedded compiler:
(1) The embedded compiler does not support __COUNTER__
(2) The invoke of bind1st() is tedious, and those redundant types in the template may get problem, if they are not synchronized.
Thus my question:
(1) How do I implement __COUNTER__ as in regular g++/gcc? Or, In fact, I only need a unique number/identifier than a counter.
(2) Is there anyway I can derive argument types and return types from the function pointer type?
(3) Even if Question (2) can be done, can I derive bare types from a reference type? For example, can I derive int from int &? Please note that the source type can be int or int &, and I need to derive the resulting type int in both cases. Please note that it's C++03, not C++11 or higher.
(4) Any other suggestions? Perhaps someone can provide a simpler or better implementation?
The goal of Q.2 and Q.3 is to make template call shorter, like FUNC ff = binder3<__COUNTER__, short(*)(int&, int const, int&)>::bind1st(f, a[i]);. All other types can be derived from the first function pointer type. In addition, I don't have to write 4 overloads.
---UPDATE---
Since the implementation uses pointers to get the binding argument and the function, it may have a potential bug/design flaw:
If the caller changes the value of the binding variable after it calls binder3<...>::bind1st(), but before the use(call) of the bound function, the effective value is the value AFTER change, not BEFORE change!
Currently I can live with it, by careful coding. And normally I use(call) the bound function right after the bind1st() (without any command in-between).
Ideas to improve this problem are welcome.
--UPDATE--
Currently, use of __COUNT__ contains a bug: Since __COUNT__ is not globally unique (restart count from zero for each compilation of .cpp file), the bounding variable may be overwritten, if they are not used promptly. If I can get a program-wide global unique number to replace __COUNT__, the above implementation should be good enough (except for those tedious stuff).
I was wondering if there is a macro or standard way (for debugging purposes) to automatically print the value of the parameters of a function f, just like __FUNCTION__ prints/shows the function signature? For example,
void foo(int x, string y) {
cout << __FUNCTIION_ARGS__ << endl;
}
should show the values of x, and y.
If there is no such magic the standard way, is it possible to write a macro/template to do this?
--Update--
Per #jxh's comment, if print inside the function in question is impossible with macro/templates, is it possible to do it automatically on the caller-side, with something like:
call(foo,x,y);
which prints every parameter value, and behaves the same with foo(x,y) as if it is called directly in every other aspect? If a value is not printable (e.g. pointers, functions), the wrapper call can just print an opaque value such as <ptr> or <noprint>.
Thanks
P.S. I am using gcc, (and also clang in the future).
My take on it :
#include <iostream>
// Dummy parameter-pack expander
template <class T>
void expand(std::initializer_list<T>) {}
// Fun
template <class Fun, class... Args>
typename std::result_of<Fun&&(Args&&...)>::type
call(Fun&& f, Args&&... args) {
// Print all parameters
std::cout << "Params : ";
expand({(std::cout << args << ' ', 0)...});
std::cout << '\n';
// Forward the call
return std::forward<Fun>(f)(std::forward<Args>(args)...);
}
// Random test function
int myFunc(std::string const &s, double d, int i) {
std::cout << s << ' ' << d << ' ' << i << '\n';
return 57;
}
int main()
{
// Painless call
std::cout << call(myFunc, "hello", 3.14, 42) << '\n';
return 0;
}
Output :
Params : hello 3.14 42
hello 3.14 42
57
Variadic templates are fun !
There is no macro for printing the arguments, but you can print the function prototype using the __PRETTY_FUNCTION__ macro
Minimal working example:
#include <tuple>
struct example
{
example(int, char) {}
};
int main()
{
std::tuple<example, int, double>
my_tuple(example(0, 'x'), 42, .0);
// std::tuple t = make_my_tuple(0, 'x');
return 0;
}
This works.
Is there a more elegant way to initialize only the first member, like I sketched in the comment? One which only takes the arguments to construct the first tuple member and does not initialize the others?
The reason I ask? I am just interested in the semantics of the language.
You say that giving values for the other two members is not necessary - are you worried about performance? Or that there may be no suitable value for these members?
If it's the latter, you could have your tuple hold boost::optionals. e.g.
#include <tuple>
#include <boost/optional.hpp>
using namespace boost;
struct example
{
example(int, char) {}
};
typedef std::tuple<example, optional<int>, optional<double>> MyTuple;
int main()
{
MyTuple my_tuple(example(0, 'x'), optional<int>(), optional<double>());
return 0;
}
You now semantically have the int and float "uninitialised", and can query their value as such.
To make this more elegant, you can wrap this into a function, using the perfect forwarding idiom for the arguments (in general; in this case your arguments are cheap to copy, so no speed benefit from doing this):
template <class... Args>
MyTuple make_mytuple(Args&&... args)
{
return MyTuple(example(std::forward<Args>(args)...), optional<int>(), optional<double));
}
The advantage of this template is that it's resilient to changes in example's constructor. If you add another argument, just call make_mytuple with the new arguments and it will work.
Your other point about the copying in the tuple construction is valid, but in reality I believe this will be optimal on most compilers. (a combination of RVO and elision of copies when passing an rvalue to a function by value).
You can use uniform initialization. Sadly, you cannot define a default value, argument will be initialized with the default constructor or 0.
#include <iostream>
#include <tuple>
enum class Result {Full, Partial, Empty};
std::tuple<bool, int, double> get_tuple(Result type)
{
if (type == Result::Full)
return {true, 42, 3.14159};
else if (type == Result::Partial)
return {true, 42, {}};
else
return {};
}
int main()
{
bool b;
int i;
double d;
std::tie(b, i, d) = get_tuple(Result::Full);
std::cout << b << " " << i << " " << d << std::endl;
std::tie(b, i, d) = get_tuple(Result::Partial);
std::cout << b << " " << i << " " << d << std::endl;
std::tie(b, i, d) = get_tuple(Result::Empty);
std::cout << b << " " << i << " " << d << std::endl;
return 0;
}
output:
1 42 3.14159
1 42 0
0 0 0