How to convert between different instantiations of the same variadic template? - c++

Assume we have a data structure Foo that maintains a set of elements. It should be possible to associate attributes with the elements as needed. The attributes should be stored in a separate vector each. We implement this by means of variadic templates:
#include <vector>
template <typename ...Attrs>
struct Foo : public Attrs... {
Foo(int n = 0) {
using PackExpansionT = int[];
PackExpansionT{0, (Attrs::values.resize(n), 0)...};
}
};
struct AttrA { std::vector<int> values; };
struct AttrB { std::vector<float> values; };
struct AttrC { std::vector<double> values; };
int main() {
Foo<AttrA, AttrB> foo; // Maintains set of elements with two attributes each.
};
Now, I want a conversion operator with the following semantics:
Foo<AttrB, AttrC> bar = foo; // bar.AttrB::values should be a copy of foo.AttrB::values.
This is only an example. In general, the conversion operator should be able to convert a Foo with arbitrary attributes into another Foo with arbitrary attributes. Attributes associated with both Foos should be copied. Attributes not associated with both can be left defaulted. However, I have no idea how to implement it.
template <typename ...OthersAttrs>
operator Foo<OthersAttrs...>() const {
// ...?
}

We can just make a bunch of independent decisions. First, let's add a constructor so that we can construct Foo from its attribute constituents:
Foo(Attrs const&... attrs)
: Attrs(attrs)...
{ }
Next, for each attribute in Others, we will either downcast this to the appropriate type if possible or return a default-constructed one otherwise:
template <typename... Others>
operator Foo<Others...>() const {
return {get_attr<Others>(this)...};
}
where:
template <class T>
T const& get_attr(T const* v) const {
return *v;
}
template <class T>
T get_attr(...) const {
return T{};
}

An outline I can think of:
template <typename ...OthersAttrs>
operator Foo<OthersAttrs...>() const
{
Foo<OthersAttrs...> rv(GetNSomehow());
(int[]){(CopyAttr<Attrs>(&rv), 0)...};
return rv;
}
template<typename Attr>
void CopyAttr(Attr *rv) const // Overload A
{
rv->values = ((const Attr*)this)->values;
}
template<typename Attr>
void CopyAttr(...) const // Overload B
{
}
The trick here is to go attribute by attribute.
If rv has the attribute the first overload will be chosen and it will be copied.
Otherwise the second overload would be chosen that will do nothing.

Related

How to expand a non value paramter pack

I've been trying to expand a non value parameter pack recently in C++. Is this possible? And if it's not, why?
I mean, as you can see, in the line with the comment //, given a parameter pack for the TypeMap class, how can I call addType<T>() with each type of the parameter pack? Thanks in advance!
template <typename... T>
class TypeMap
{
using vari_cmps = std::variant<T*...>;
private:
template<typename Type>
void addType()
{
typemap[typeid(Type).name()] = std::make_unique<Type>(0).get();
}
public:
std::map<const char*, vari_cmps> typemap{};
TypeMap()
{
(addType<T,...>()); // Idk how to use this in order to make it work
}
~TypeMap()
{
typemap.clear();
}
};
As #HolyBlackCat has already answered in the comments, you can expand it like this:
TypeMap() {
(addType<T>(), ...);
}
If T is std::string, int, float this would expand to:
TypeMap() {
(addType<std::string>(), addType<int>(), addType<float>());
}
There are however a few more issues in this code-snippet:
1. addType()
addType() will not work as you'd expect, due to the unique_ptr deleteing your object after you put it into the map.
.get() only retrieves the pointer that the unique_ptr manages but does not transfer ownership, so the unique_ptr will still delete the pointed-to object once it gets out of scope, leaving a dangling pointer in your map.
so your addType() is roughly equivalent to:
template<typename Type>
void addType() {
Type* tptr = new Type(0); // unique pointer constructs object
typemap[typeid(Type).name()] = tptr; // insert pointer value of unique pointer
delete tptr; // unique pointer destructs
}
You could fix this by releasing the unique_ptr after inserting its value into the map & then cleaning it up in the destructor:
template<typename Type>
void addType() {
auto ptr = std::make_unique<Type>(0);
typemap[typeid(Type).name()] = ptr.get();
ptr.release(); // now unique_ptr won't delete the object
}
~TypeMap() {
// cleanup all pointers
for(auto& [name, ptrVariant] : typemap)
std::visit([](auto ptr) { delete ptr; }, ptrVariant);
}
2. Consider using std::type_index instead of const char* as map key
std::type_info::name() returns an implementation-defined name for the given type, so you have no guarantee that you will get an unique name for a given type.
Returns an implementation defined null-terminated character string containing the name of the type. No guarantees are given; in particular, the returned string can be identical for several types and change between invocations of the same program.
std::type_index on the other hand is build specifically for this purpose - using types as keys - and comes with all comparison operators & a std::hash specialization, so you can use it with std::map & std::unordered_map out of the box.
e.g.:
template <class... T>
class TypeMap
{
using vari_cmps = std::variant<T*...>;
private:
template<typename Type>
void addType()
{
typemap[std::type_index(typeid(Type))] = /* something */;
}
public:
std::map<std::type_index, vari_cmps> typemap{};
TypeMap() { /* ... */ }
~TypeMap() { /* ... */ }
template<class U>
U* get() {
auto it = typemap.find(std::type_index(typeid(U)));
return std::get<U*>(it->second);
}
};
Consider using std::tuple
std::tuple is basically built for this task, storing a list of arbitrary types:
e.g.:
template <class... T>
class TypeMap
{
private:
std::tuple<std::unique_ptr<T>...> values;
public:
TypeMap() : values(std::make_unique<T>(0)...) {
}
template<class U> requires (std::is_same_v<U, T> || ...)
U& get() { return *std::get<std::unique_ptr<U>>(values); }
template<class U> requires (std::is_same_v<U, T> || ...)
U const& get() const { return *std::get<std::unique_ptr<U>>(values); }
};
usage:
TypeMap<int, double, float> tm;
tm.get<int>() = 12;
If you want you can also store T's directly in the tuple, avoiding the additional allocations.

Is it possible to omit the template parameter when i have the type on the left hand side of the assignment?

I want to write a function that returns a preallocated vector.
Since it should work for any kind of vector, I am using a template.
My question is whether it is possible to deduce the template parameter of the function call with the template parameter of the left hand side vector ?
template<typename T>
std::vector<T> createVector(size_t initial) {
std::vector<T> vec;
vec.reserve(initial);
return vec;
}
struct Foo {
};
int main() {
//std::vector<Foo> foos = createVector(4); // this should work
//auto foos = createVector<Foo>(4); // this should be okay, too
std::vector<Foo> foos = createVector<Foo>(4); //duplicate Type :(
}
Is it possible to deduce the template parameter of the function call with the template parameter of the left hand side vector ?
No, except for conversion operator (for classes):
struct CreateVector
{
std::size_t m_capacity;
CreateVector(std::size_t capacity) : m_capacity(capacity) {}
template <typename T>
operator std::vector<T>() const
{
std::vector<T> vec;
vec.reserve(m_capacity);
return vec;
}
};
and then
struct Foo {};
int main() {
[[maybe_unused]]std::vector<Foo> foos = CreateVector(4);
}
Demo

How do I use a templated class within a new template?

Not sure why, but the code in xcode just started working. I am very happy.
Thank you all for your time.
//here is JUCE's code
//==============================================================================
/** This template-overloaded class can be used to convert between var and custom types.
#tags{Core}
*/
template <class Type> struct VariantConverter
{
static Type fromVar (const var& v) { return static_cast<Type> (v); }
static var toVar (const Type& t) { return t; }
};
//here is my addition
template <> struct VariantConverter<Point<int>>
{
static Point<int> fromVar (const var& v)
{
return Point<int>
(
{ v[0].operator int(), v[1].operator int() }
);
}
static var toVar (const Point<int>& p)
{
return Array<var>({ p.getX(), p.getY() });
}
};
Hi, I am not sure how I can achieve the above code. A class like typedef to replace the
Point<int>
would be great. I am appending a JUCE file.
Xcode gives me two errors
/Tools/JUCE/modules/juce_core/containers/juce_Variant.h:41:18: Expected unqualified-id
and
/Tools/JUCE/modules/juce_core/containers/juce_Variant.h:343:46: Expected '>'
Thank you
As there are only static methods you don't need an Object of that type. You should be able to call it like:
typedef Point<int> MyPoint;
MyPoint point(1,2); // call constructor of a Point<int>
var x = VariantConverter::toVar(point); //call static methods of struct
MyPoint point2 = VariantConverter::fromVar(x);
template <>
struct VariantConverter<Point<int>>
{ /* ... */ };
What you have here is a template specialisation. If you want to specialise, you first need a base template before you can compile.
template <typename>
struct VariantConverter;
Sometimes it is useful if only the specialisations exist, then you would just declare the base as above, fare mor common, though, is having a default implementation:
template <typename T>
struct VariantConverter
{
static T fromVar(var const& v);
};
Solely, what shall var be in above example? T as well? Another template parameter of VariantConverter struct? Third option is the one I show: function template parameter:
template <typename T>
struct VariantConverter
{
template <typename var>
static T fromVar(var const& v);
};
You now can provide a default implementation suitable for the majority of types – if there is a reasonable one (otherwise, it might be more appropriate not to implement the default and just have the specialisations).
OK, now we can specialise:
template <>
struct VariantConverter<Point<int>>
// Point<int> now is what previously was T; could be any arbitrary type, not
// necessarily Point<int>, but std::string, double, ...
{
// still, you need to tell what var actually is
// maybe you have defined a class for already? similar to std::vector?
// assuming so this time...
static Point<int> fromVar (var const& v)
{
return Point<int>
(
// are you sure point accepts an initializer list?
// or do you intend to create first a point and then
// use move/copy constructor???
//{ v[0].operator int(), v[1].operator int() }
// rather normal constructor directly:
//v[0].operator int(), v[1].operator int()
// however, explicitly calling cast operators is ugly, rather just cast:
static_cast<int>(v[0]), static_cast<int>(<v[1])
// or C style casts, if you prefer
//(int)v[0], (int)v[1]
// I personally would rather go with the former, though, for being
// more "C++-ish"
);
}
};
Now maybe you are looking for something totally different:
template <typename T>
struct VariantConverter
{
static Point<T> fromVar(std::vector<T> const& v)
{
return Point<T>(v[0], v[1]);
}
};
Finally, as you explicitly asked for:
template <>
struct VariantConverter<Point<int>>
{
using Type = Point<int>;
static Type fromVar(/* ... */);
};
or, for second variant:
template <typename T>
struct VariantConverter
{
using Type = Point<T>;
static Type fromVar( /* ... */);
};
Sorry for my poor communication. English is my first language and despite being educated, I am not gifted at written communication. Here is the bigger context. I am trying to add to an existing class.
//This is the end of their code
//==============================================================================
/** This template-overloaded class can be used to convert between var and custom types.
#tags{Core}
*/
template <class Type> struct VariantConverter
{
static Type fromVar (const var& v) { return static_cast<Type> (v); }
static var toVar (const Type& t) { return t; }
};
//here is my code
template <> struct VariantConverter<Point<int>>
{
static Point<int> fromVar (const var& v)
{
return Point<int>
(
{ v[0].operator int(), v[1].operator int() }
);
}
static var toVar (const Point<int>& p)
{
return Array<var>({ p.getX(), p.getY() });
}
};

Assigning a template-generated class to a C struct with the same layout

If I understand correctly, the object ’A’ defined thus:
typedef struct {
int n;
float *p;
} myStruct;
myStruct A;
is an aggregate with exactly the same layout in memory as the object ‘B’ defined as:
template <typename T> class myTemplateClass
{
public:
int n;
T* p;
};
myTemplateClass<float> B;
So, is there a more elegant way of assigning
A = B;
than having to write
A = *(reinterpret_cast< myStruct *>(&B));
every time?
My reason for asking is that I have to call a library function which exposes an interface with arguments of the form ‘myStruct’, from code where holding my data in the form of myTemplateClass is a great deal more natural.
This requires a bit of boilerplate. Two functions per myStruct, and two functions per template.
In the namespace of myStruct inject these two functions:
auto members( myStruct& s ) {
return std::tie(s.n, s.p);
}
auto members( myStruct const& s ) {
return std::tie(s.n, s.p);
}
in C++11 you have to add a decltype clause to give the return value explicitly. Basically tie the members in the exact order declared.
In the body of the myTemplateClass, declare a friend function members that does something similar:
template <typename T>
class myTemplateClass {
public:
int n;
T* p;
friend auto members( myTemplateClass<T>& self ) {
return std::tie( self.n, self.p );
}
friend auto members( myTemplateClass<T> const& self ) {
return std::tie( self.n, self.p );
}
};
finally, write assign:
template<class Lhs, class Rhs>
void assign_by_members( Lhs& lhs, Rhs const& rhs ) {
members(lhs) = members(rhs);
}
and we are done.
Anyone that declares a free function members that returns something assignable to the other members works. tie does element-wise assign on references, so all is good.
Note that only the one being assigned from needs the const& overload of members and only the one being assigned to needs the & overload of members. So if assignment always goes from template class to C-struct, you can halve that boilerplate.
If you don't like the assign_by_members syntax, you can override operator O as follows:
template <typename T>
class myTemplateClass {
public:
// ... see above for code that goes here
template<class O,class=decltype(members(std::declval<O>())>
operator O() const {
O retval;
assign_by_members( retval, *this );
return retval;
}
};
which also does a test to determine if the type converted to supports members. You could go a step further and test if the members return value can be assigned to from the return value of members(*this), but that adds more boilerplate.
You could make myTemplateClass<T> derive from the correct myStruct depending on the type parameter. You can use template specialization for this:
template <typename T> class myTemplateClass;
// Specialization for float
template <> class myTemplateClass<float> : public myStruct {};
// Specialization for int
template <> class myTemplateClass<int> : public myOtherStruct {};
// And so on for other types...
This way, you can assign instances of myTemplateClass<float> to myStruct, and myTemplateClass<int> to myOtherStruct. (Bonus: You don't have to rely on the "same memory layout" guess.)
If you derive from mystruct, then, u can use a static_cast on it.
As suggested above, template specialization would work too.
I would take static_cast over reinterpret_cast any day.
Below is the working code.
typedef struct {
int n;
float *p;
} myStruct;
myStruct A;
template <typename T> class myTemplateClass:public myStruct
{
public:
int n;
T* p;
};
//myTemplateClass<float> B;
typedef myTemplateClass<float> temp;
temp B;
int main()
{
A = *(static_cast< myStruct *>(&B));

how to avoid undefined execution order for the constructors when using std::make_tuple

How can I use std::make_tuple if the execution order of the constructors is important?
For example I guess the execution order of the constructor of class A and the constructor of class B is undefined for:
std::tuple<A, B> t(std::make_tuple(A(std::cin), B(std::cin)));
I came to that conclusion after reading a comment to the question
Translating a std::tuple into a template parameter pack
that says that this
template<typename... args>
std::tuple<args...> parse(std::istream &stream) {
return std::make_tuple(args(stream)...);
}
implementation has an undefined execution order of the constructors.
Update, providing some context:
To give some more background to what I am trying to do, here is a sketch:
I want to read in some serialized objects from stdin with the help of CodeSynthesis XSD binary parsing/serializing. Here is an example of how such parsing and serialization is done: example/cxx/tree/binary/xdr/driver.cxx
xml_schema::istream<XDR> ixdr (xdr);
std::auto_ptr<catalog> copy (new catalog (ixdr));
I want to be able to specify a list of the classes that the serialized objects have (e.g. catalog, catalog, someOtherSerializableClass for 3 serialized objects) and store that information as a typedef
template <typename... Args>
struct variadic_typedef {};
typedef variadic_typedef<catalog, catalog, someOtherSerializableClass> myTypes;
as suggested in Is it possible to “store” a template parameter pack without expanding it?
and find a way to get a std::tuple to work with after the parsing has finished. A sketch:
auto serializedObjects(binaryParse<myTypes>(std::cin));
where serializedObjects would have the type
std::tuple<catalog, catalog, someOtherSerializableClass>
The trivial solution is not to use std::make_tuple(...) in the first place but to construct a std::tuple<...> directly: The order in which constructors for the members are called is well defined:
template <typename>
std::istream& dummy(std::istream& in) {
return in;
}
template <typename... T>
std::tuple<T...> parse(std::istream& in) {
return std::tuple<T...>(dummy<T>(in)...);
}
The function template dummy<T>() is only used to have something to expand on. The order is imposed by construction order of the elements in the std::tuple<T...>:
template <typename... T>
template <typename... U>
std::tuple<T...>::tuple(U...&& arg)
: members_(std::forward<U>(arg)...) { // NOTE: pseudo code - the real code is
} // somewhat more complex
Following the discussion below and Xeo's comment it seems that a better alternative is to use
template <typename... T>
std::tuple<T...> parse(std::istream& in) {
return std::tuple<T...>{ T(in)... };
}
The use of brace initialization works because the order of evaluation of the arguments in a brace initializer list is the order in which they appear. The semantics of T{...} are described in 12.6.1 [class.explicit.init] paragraph 2 stating that it follows the rules of list initialization semantics (note: this has nothing to do with std::initializer_list which only works with homogenous types). The ordering constraint is in 8.5.4 [dcl.init.list] paragraph 4.
As the comment says, you could just use initializer-list:
return std::tuple<args...>{args(stream)...};
which will work for std::tuple and suchlikes (which supports initializer-list).
But I got another solution which is more generic, and can be useful where initializer-list cannot be used. So lets solve this without using initializer-list:
template<typename... args>
std::tuple<args...> parse(std::istream &stream) {
return std::make_tuple(args(stream)...);
}
Before I explain my solution, I would like to discuss the problem first. In fact, thinking about the problem step by step would also help us to come up with a solution eventually. So, to simply the discussion (and thinking-process), lets assume that args expands to 3 distinct types viz. X, Y, Z, i.e args = {X, Y, Z} and then we can think along these lines, reaching towards the solution step-by-step:
First and foremost, the constructors of X, Y, and Z can be executed in any order, because the order in which function arguments are evaluated is unspecified by the C++ Standard.
But we want X to construct first, then Y, and Z. Or at least we want to simulate that behavior, which means X must be constructed with data that is in the beginning of the input stream (say that data is xData) and Y must be constructed with data that comes immediately after xData, and so on.
As we know, X is not guaranteed to be constructed first, so we need to pretend. Basically, we will read the data from the stream as if it is in the beginning of the stream, even if Z is constructed first, that seems impossible. It is impossible as long as we read from the input stream, but we read data from some indexable data structure such as std::vector, then it is possible.
So my solution does this: it will populate a std::vector first, and then all arguments will read data from this vector.
My solution assumes that each line in the stream contains all the data needed to construct an object of any type.
Code:
//PARSE FUNCTION
template<typename... args>
std::tuple<args...> parse(std::istream &stream)
{
const int N = sizeof...(args);
return tuple_maker<args...>().make(stream, typename genseq<N>::type() );
}
And tuple_maker is defined as:
//FRAMEWORK - HELPER ETC
template<int ...>
struct seq {};
template<int M, int ...N>
struct genseq : genseq<M-1,M-1, N...> {};
template<int ...N>
struct genseq<0,N...>
{
typedef seq<N...> type;
};
template<typename...args>
struct tuple_maker
{
template<int ...N>
std::tuple<args...> make(std::istream & stream, const seq<N...> &)
{
return std::make_tuple(args(read_arg<N>(stream))...);
}
std::vector<std::string> m_params;
std::vector<std::unique_ptr<std::stringstream>> m_streams;
template<int Index>
std::stringstream & read_arg(std::istream & stream)
{
if ( m_params.empty() )
{
std::string line;
while ( std::getline(stream, line) ) //read all at once!
{
m_params.push_back(line);
}
}
auto pstream = new std::stringstream(m_params.at(Index));
m_streams.push_back(std::unique_ptr<std::stringstream>(pstream));
return *pstream;
}
};
TEST CODE
///TEST CODE
template<int N>
struct A
{
std::string data;
A(std::istream & stream)
{
stream >> data;
}
friend std::ostream& operator << (std::ostream & out, A<N> const & a)
{
return out << "A" << N << "::data = " << a.data ;
}
};
//three distinct classes!
typedef A<1> A1;
typedef A<2> A2;
typedef A<3> A3;
int main()
{
std::stringstream ss("A1\nA2\nA3\n");
auto tuple = parse<A1,A2,A3>(ss);
std::cout << std::get<0>(tuple) << std::endl;
std::cout << std::get<1>(tuple) << std::endl;
std::cout << std::get<2>(tuple) << std::endl;
}
Output:
A1::data = A1
A2::data = A2
A3::data = A3
which is expected. See demo at ideone yourself. :-)
Note that this solution avoids the order-of-reading-from-the-stream problem by reading all the lines in the first call to read_arg itself, and all the later calls just read from the std::vector, using the index.
Now you can put some printf in the constructor of the classes, just to see that the order of construction is not same as the order of template arguments to the parse function template, which is interesting. Also, the technique used here can be useful for places where list-initialization cannot be used.
There's nothing special about make_tuple here. Any function call in C++ allows its arguments to be called in an unspecified order (allowing the compiler freedom to optimize).
I really don't suggest having constructors that have side-effects such that the order is important (this will be a maintenance nightmare), but if you absolutely need this, you can always construct the objects explicitly to set the order you want:
A a(std::cin);
std::tuple<A, B> t(std::make_tuple(a, B(std::cin)));
This answer comes from a comment I made to the template pack question
Since make_tuple deduces the tuple type from the constructed components and function arguments have undefined evaluation ordder, the construction has to happen inside the machinery, which is what I proposed in the comment. In that case, there's no need to use make_tuple; you could construct the tuple directly from the tuple type. But that doesn't order construction either; what I do here is construct each component of the tuple, and then build a tuple of references to the components. The tuple of references can be easily converted to a tuple of the desired type, provided the components are easy to move or copy.
Here's the solution (from the lws link in the comment) slightly modified, and explained a bit. This version only handles tuples whose types are all different, but it's easier to understand; there's another version below which does it correctly. As with the original, the tuple components are all given the same constructor argument, but changing that simply requires adding a ... to the lines indicated with // Note: ...
#include <tuple>
#include <type_traits>
template<typename...T> struct ConstructTuple {
// For convenience, the resulting tuple type
using type = std::tuple<T...>;
// And the tuple of references type
using ref_type = std::tuple<T&...>;
// Wrap each component in a struct which will be used to construct the component
// and hold its value.
template<typename U> struct Wrapper {
U value;
template<typename Arg>
Wrapper(Arg&& arg)
: value(std::forward<Arg>(arg)) {
}
};
// The implementation class derives from all of the Wrappers.
// C++ guarantees that base classes are constructed in order, and
// Wrappers are listed in the specified order because parameter packs don't
// reorder.
struct Impl : Wrapper<T>... {
template<typename Arg> Impl(Arg&& arg) // Note ...Arg, ...arg
: Wrapper<T>(std::forward<Arg>(arg))... {}
};
template<typename Arg> ConstructTuple(Arg&& arg) // Note ...Arg, ...arg
: impl(std::forward<Arg>(arg)), // Note ...
value((static_cast<Wrapper<T>&>(impl)).value...) {
}
operator type() const { return value; }
ref_type operator()() const { return value; }
Impl impl;
ref_type value;
};
// Finally, a convenience alias in case we want to give `ConstructTuple`
// a tuple type instead of a list of types:
template<typename Tuple> struct ConstructFromTupleHelper;
template<typename...T> struct ConstructFromTupleHelper<std::tuple<T...>> {
using type = ConstructTuple<T...>;
};
template<typename Tuple>
using ConstructFromTuple = typename ConstructFromTupleHelper<Tuple>::type;
Let's take it for a spin
#include <iostream>
// Three classes with constructors
struct Hello { char n; Hello(decltype(n) n) : n(n) { std::cout << "Hello, "; }; };
struct World { double n; World(decltype(n) n) : n(n) { std::cout << "world"; }; };
struct Bang { int n; Bang(decltype(n) n) : n(n) { std::cout << "!\n"; }; };
std::ostream& operator<<(std::ostream& out, const Hello& g) { return out << g.n; }
std::ostream& operator<<(std::ostream& out, const World& g) { return out << g.n; }
std::ostream& operator<<(std::ostream& out, const Bang& g) { return out << g.n; }
using std::get;
using Greeting = std::tuple<Hello, World, Bang>;
std::ostream& operator<<(std::ostream& out, const Greeting &n) {
return out << get<0>(n) << ' ' << get<1>(n) << ' ' << get<2>(n);
}
int main() {
// Constructors run in order
Greeting greet = ConstructFromTuple<Greeting>(33.14159);
// Now show the result
std::cout << greet << std::endl;
return 0;
}
See it in action on liveworkspace. Verify that it constructs in the same order in both clang and gcc (libc++'s tuple implementation holds tuple components in the reverse order to stdlibc++, so it's a reasonable test, I guess.)
To make this work with tuples which might have more than one of the same component, it's necessary to modify Wrapper to be a unique struct for each component. The easiest way to do this is to add a second template parameter, which is a sequential index (both libc++ and libstdc++ do this in their tuple implementations; it's a standard technique). It would be handy to have the "indices" implementation kicking around to do this, but for exposition purposes, I've just done a quick-and-dirty recursion:
#include <tuple>
#include <type_traits>
template<typename T, int I> struct Item {
using type = T;
static const int value = I;
};
template<typename...TI> struct ConstructTupleI;
template<typename...T, int...I> struct ConstructTupleI<Item<T, I>...> {
using type = std::tuple<T...>;
using ref_type = std::tuple<T&...>;
// I is just to distinguish different wrappers from each other
template<typename U, int J> struct Wrapper {
U value;
template<typename Arg>
Wrapper(Arg&& arg)
: value(std::forward<Arg>(arg)) {
}
};
struct Impl : Wrapper<T, I>... {
template<typename Arg> Impl(Arg&& arg)
: Wrapper<T, I>(std::forward<Arg>(arg))... {}
};
template<typename Arg> ConstructTupleI(Arg&& arg)
: impl(std::forward<Arg>(arg)),
value((static_cast<Wrapper<T, I>&>(impl)).value...) {
}
operator type() const { return value; }
ref_type operator()() const { return value; }
Impl impl;
ref_type value;
};
template<typename...T> struct List{};
template<typename L, typename...T> struct WrapNum;
template<typename...TI> struct WrapNum<List<TI...>> {
using type = ConstructTupleI<TI...>;
};
template<typename...TI, typename T, typename...Rest>
struct WrapNum<List<TI...>, T, Rest...>
: WrapNum<List<TI..., Item<T, sizeof...(TI)>>, Rest...> {
};
// Use WrapNum to make ConstructTupleI from ConstructTuple
template<typename...T> using ConstructTuple = typename WrapNum<List<>, T...>::type;
// Finally, a convenience alias in case we want to give `ConstructTuple`
// a tuple type instead of a list of types:
template<typename Tuple> struct ConstructFromTupleHelper;
template<typename...T> struct ConstructFromTupleHelper<std::tuple<T...>> {
using type = ConstructTuple<T...>;
};
template<typename Tuple>
using ConstructFromTuple = typename ConstructFromTupleHelper<Tuple>::type;
With test here.
I believe the only way to manually unroll the definition. Something like the following might work. I welcome attempts to make it nicer though.
#include <iostream>
#include <tuple>
struct A { A(std::istream& is) {}};
struct B { B(std::istream& is) {}};
template <typename... Ts>
class Parser
{ };
template <typename T>
class Parser<T>
{
public:
static std::tuple<T> parse(std::istream& is) {return std::make_tuple(T(is)); }
};
template <typename T, typename... Ts>
class Parser<T, Ts...>
{
public:
static std::tuple<T,Ts...> parse(std::istream& is)
{
A t(is);
return std::tuple_cat(std::tuple<T>(std::move(t)),
Parser<Ts...>::parse(is));
}
};
int main()
{
Parser<A,B>::parse(std::cin);
return 1;
}