Implementation of typed tuple wrapper - c++

How can an implementation look like, that wraps around e.g. a std::tuple as a static list of type/value, plus a type (not contained in tuple) to refer to some kind of owner/visitor.
I want to instantiate like
constexpr auto data = data_of<a>(1, 2.0);
Background
The idea is to use a data_of<T>(...) kind of structure to to pass a list of type/data pairs into a bulk function like this.
template <typename... _Ts>
static constexpr void do_bulk_stuff(_Ts&&... _Vs)
{
// crazy stuff happens here
}
// call it like
do_bulk_stuff
(
data_of<a>(1, 2.0),
data_of<b>(3),
data_of<c>(4.0, 5, 6),
// ...
);
Attempt 1
So far I ended up with a naive (not working) implementation attempt like
template <typename T, typename... Ts>
struct data_of {
using type = T;
using data_t = std::tuple<Ts...>;
data_t data;
constexpr data_of(Ts&&... Vs)
: data(Vs...)
{}
};
Goal
My goal is to achieve a result like this data_of<a> instance pseudo code example
{
// meta
type = a;
data_t = std::tuple<int,double>;
// runtime
data = [1, 2.0];
}

The issue inherent to your attempt is that class template argument deduction is simply not like its function counterpart. There will be no deduction if any of the class template's arguments is explicitly specified. You fail because the trailing pack is always specified (by omission) as empty.
The solution is to shift the burden onto the mechanism that allows you to specify only part of the arguments: function templates.
template <typename T, typename... Ts>
struct data_of_t {
using type = T;
using data_t = std::tuple<Ts...>;
data_t data;
constexpr data_of_t(Ts&&... vs)
: data(std::forward<Ts>(vs)...)
{}
};
template<typename T, typename... Ts>
constexpr auto data_of(Ts&&... vs) {
return data_of_t<T, Ts...>(std::forward<Ts>(vs)...);
}
Now the type of the expression data_of<a>(1, 2.0) has the same "meta" properties you are after.

Related

Compile-time generation of array using an interleaved formatter

I am trying to build a compile-time generated array of a certain type, based on a template parameter pack of some types. The array generation works just fine if the types are used directly. However, I'd like to apply a tag on each each and have them be interleaved in the generated array.
So, this is where I am at:
template <typename Head, typename... Tail>
static constexpr attributes_t make_attrs = {...};
and instantiated by (size is calculated elsewhere)
attributes_t attributes[sizeof...(Ts)] = {make_attrs<Ts>...};
What I would really like is to be able to make the latter tag each type in Ts, say with First and Second tags, such that I can produce a type-list like this
NewTs = First<Ts[0]>, Second<Ts[0]>, First<Ts[1]>, Second<Ts[1]>, First<Ts[2]>, Second<Ts[2]>, ...
It's important that the First and Second taggers are interleaved like that. The intent is to be able to control the instantiation of each one by its tagged version.
In my naivity I tried something like this
attributes_t attributes[2 * sizeof...(Ts)] = {make_attrs<First<Ts>, Second<Ts>>...};
in the hopes that it would expand the pack in the interleaved pattern. Unfortunately that didn't work - I am hoping that there exists a similarly simple and concise solution :)
There must be a way to do this, I'm sure. I have looked through various posts here and there and cannot find a solution that has worked yet, or perhaps I just didn't quite understand it.
I hope you have a great solution :)
As usual std::index_sequence might help:
template <typename T> struct First{};
template <typename T> struct Second{};
template <typename... Ts>
struct Types
{
};
template <typename Seq, typename Tuple>
struct Impl;
template <std::size_t... Is, typename Tuple>
struct Impl<std::index_sequence<Is...>, Tuple>
{
using type =
Types<std::conditional_t<Is % 2 == 0,
First<std::tuple_element_t<Is / 2, Tuple>>,
Second<std::tuple_element_t<Is / 2, Tuple>>>...>;
};
template <typename... Ts>
using makeRes = typename Impl<std::make_index_sequence<2 * sizeof...(Ts)>,
std::tuple<Ts...>>::type;
static_assert(std::is_same_v<Types<First<int>, Second<int>,
First<char>, Second<char>>,
makeRes<int, char>>);
Demo
if attributes_t is default constructible, assigning might be shorter:
template <typename... Ts>
constexpr std::array<attributes_t, 2 * sizeof...(Ts)> make_attributes()
{
std::array<attributes_t, 2 * sizeof...(Ts)> res{};
std::size_t index = 0;
((attributes[index++] = make_attrs<First<Ts>>,
attributes[index++] = make_attrs<Second<Ts>>),
...);
return res;
}
Folding expression is C++17, you can workaround that in C++11/C++14 with initializer_list trick:
template <typename... Ts>
constexpr std::array<attributes_t, 2 * sizeof...(Ts)> make_attributes()
{
std::array<attributes_t, 2 * sizeof...(Ts)> res{};
std::size_t index = 0;
const int dummy[] = {0, (
attributes[index++] = make_attrs<First<Ts>>,
attributes[index++] = make_attrs<Second<Ts>>,
0)...};
static_cast<void>(dummy); // silent warning for unused variable
return res;
}

Converting std::type_identity object to a type

Suppose that we create two type_of functions that return std::type_identity, like:
template<auto VAR>
auto type_of() {
return std::type_identity<decltype(VAR)>{};
}
template<typename T>
auto type_of() {
return std::type_identity<T>{};
}
The way to get an actual type from std::type_identity seems a bit cumbersome:
// this works
// both i1 and i2 are ints
decltype(type_of<int>())::type i1;
decltype(type_of<int{}>())::type i2;
Is there a way to waive the need for decltype in above expressions, or to put it inside a reusable expression, to achieve something nicer like:
// can this work?
type_of<int>()::type i1;
type_of<int{}>()::type i2;
Or even better:
// can this work??
type_of_t<int> i1;
type_of_t<int{}> i2;
Note: specialization for type and non-type template parameter, which could have been a direction, doesn't work (cannot compile):
template<auto>
struct type_of;
template<typename T>
struct type_of<T> { // <== compilation error: type/value mismatch
using type = T;
};
template<auto VAR>
struct type_of<VAR> {
using type = decltype(VAR);
};
You can create a type alias. However you can't "overload" it. So my solution is to create two:
template <auto Var>
using type_of_var_t = decltype(type_of<Var>())::type;
template <class T>
using type_of_t = decltype(type_of<T>())::type;
auto test()
{
type_of_var_t<11> i1 = 24;
type_of_t<int> i2 = 17;
}
In C++, a template parameter must be a value, a type, or another template (which itself must fit within the declared template header). It must be exactly one of these.
If you want to do this:
the idea is to get something that is unaware on the caller side whether the template parameter is a type or a variable
The basic requirement for being able to do this is to write a template with a parameter that could be a value or a type.
That's not a thing C++ allows.
Template function overloading allows you to get away with something like that. But that only works because it isn't one template. It's two templates that are overloaded. Which one gets selected depends on the template arguments provided.
Template classes can't be overloaded. And template specialization cannot change the nature of the original template (like what its template parameters are). It can only allow you to reinterpret the template parameters of the original template parameters in order to provide an alternative implementation.
If you want this, you're going to have to wait until either C++ gets the ability to have a template parameter that could be anything or until C++ gets the ability to convert types into values and back (ie: reflection).
Getting the type from an std::type_identity object can be encapsulated into the following expresion:
template<auto x>
using type = typename decltype(x)::type;
This would allow to replace the cumbersome expressions:
decltype(type_of<int>())::type i1;
decltype(type_of<int{}>())::type i2;
With a more simple expression:
type<type_of<int>()> i1;
type<type_of<int{}>()> i2;
It still requires to go through two steps (type_of then type) as the first one shall be able to get a type or a variable, which is applicable only with function template overloading, then the function cannot return a type, so it returns an object that needs a template expression to extract the inner type.
Depending on what you want to do with the type, the code can become even simpler.
If all you want is to create an object of that type, you can forward the creation of the object into the function:
template<auto VAR, typename... Args>
auto create_type_of(Args&&... args) {
return decltype(VAR){std::forward<Args>(args)...};
}
template<typename T, typename... Args>
auto create_type_of(Args&&... args) {
return T{std::forward<Args>(args)...};
}
auto i1 = create_type_of<int>(7);
auto i2 = create_type_of<int{}>(7);
The generic case of creating a type from std::type_identity can work this way:
template<auto VAR>
constexpr auto type_of() {
return std::type_identity<decltype(VAR)>{};
}
template<typename T>
constexpr auto type_of() {
return std::type_identity<T>{};
}
template<typename T, typename... Args>
auto create(std::type_identity<T>, Args&&... args) {
return T{std::forward<Args>(args)...};
}
auto i1 = create(type_of<int>(), 7);
auto i2 = create(type_of<int{}>(), 7);
Note that the entire thing works only with variables that can be used as non-type template parameters. For a more generic approach that works without templates (but with a macro...), and thus can work for variables that cannot be template parameters, see this mind blowing neat answer!
By design, template arguments in C++ templates are either templates, types or values.
Within the template, you know which they are. All expressions within the template that are dependent on the template arguments are disambiguated using typename or template keywords (or context) so their category is known before arguments are substituted.
Now there are metaprogramming libraries that work with value-substitutes for all 3.
template<class T> struct type_tag_t {using type=T;};
template<class T> constexpr type_tag_t<T> tag={};
template<template<class...>class Z> struct template_z_t {
template<class...Ts>
using result = Z<Ts...>;
template<class...Ts>
constexpr result<Ts...> operator()( type_tag_t<Ts>... ) const { return {}; }
};
template<template<class...>class Z>
constexpr template_z_t<Z> template_z = {};
here templates are mapped to constexpr function objects. The template_z template lets you map type-templates over to this domain.
template<auto x>
using type = typename decltype(x)::type;
template<auto x>
constexpr std::integral_constant<std::decay_t<decltype(x)>, x> k = {};
So,
constexpr auto vector_z = template_z<std::vector>;
constexpr auto vector_tag = vector_z( tag<int>, tag<std::allocator<int>> );
then you can go back to the type:
type<vector_tag> v{1,2,3,4};
this probably isn't what you want.
You might be willing to check the proposal for Universal Template Parameters
The implication example quite match your use-case of specializing for both TTP and NTTP :
template <template auto>
struct X;
template <typename T>
struct X<T> {
// T is a type
using type = T;
};
template <auto val>
struct X<val> : std::integral_constant<decltype(val), val> {
// val is an NTTP
};

Distribute template wrapper across parameter pack

I have a couple of templated types, Egg<T> and Chick<T>.
template<typename T>
struct Egg{};
template<typename T>
struct Chick{};
The chicks are contained in a class LoudNest<Chicks...> and the eggs in QuietNest<Eggs...>:
template <typename... Chicks>
struct LoudNest {
std::tuple<Chicks...> chicks;
};
template <typename... Eggs>
struct QuietNest {
std::tuple<Eggs...> eggs;
// more here.
};
I want to have a hatch method on QuietNest<Eggs...> that produces a LoudNest. The LoudNest should have a Chick<T> for each Egg<T> in the QuietNest. I have a function QuietNest<Eggs...>::hatch_impl that can create a std::tuple<Chicks...> where the Chicks all have the correct type parameters. That is, QuietNest<Egg<double>, Egg<string>, Egg<char>>::hatch_impl will return std::tuple<Chick<double>, Chick<string>, Chick<char>>. I'm getting stuck trying to wrap that in a LoudNest constructor:
template <typename... Eggs>
struct QuietNest {
std::tuple<Eggs...> eggs;
auto hatch() const {
// hatchlings is a std::tuple of chicks templated how I want.
auto hatchlings = std::apply(
[&](auto... args) { return hatch_impl(std::make_tuple(), args...); },
eggs);
// This line causes an error:
return LoudNest{hatchlings};
// error: cannot refer to class template 'LoudNest' without a template argument
}
// The rest of this all works, but is included in case you want to poke at it:
// base case: only one parameter was passed—the tuple of hatched chicks.
template<typename...Chicks>
std::tuple<Chicks...> hatch_impl(std::tuple<Chicks...> chicks) {
return chicks;
}
// recursive case: in addition to the tuple of hatched chicks,
// at least one egg was passed (possibly more in the tail)
template<typename...Chicks, typename T, typename...Unhatched>
std::tuple<Chicks..., Chick<T>> hatch_impl(
std::tuple<Chicks...> chicks,
const Egg<T>& egg,
Unhatched... tail
) const {
Chick<T> babyBird = hatchOne(egg);
return hatch_impl(
std::tuple_cat(chicks, std::make_tuple(babyBird)),
tail...);
}
template<T>
Chick<T> hatchOne(Egg<T> egg) { return Chick<T>{}; }
};
I'm thinking I need to make a "converter" that accepts a parameter pack of eggs and produces a LoudNest with chicks of the corresponding types. Starting with converting a single Egg<T> to a Chick<T>, I have:
template<typename T>
struct AsChick {
using type = T;
};
template< template <typename> class E, typename T>
struct AsChick<E<T>> {
static_assert(std::is_same<E<T>, Egg<T>>::value, "Expected AsChick to be used with an Egg<T>");
using type = Chick<T>;
};
Where I'm getting stuck is when I try to do the same for a parameter pack:
template<typename... Eggs>
struct AsLoudNest1 {
using type = LoudNest<
(AsChick<Eggs>::type)...
// I want this to expand Eggs to produce
// AsChick<Eggs0>::type, AsChick<Eggs1>::type, AsChick<Eggs2>::type, ...
// but it doesn't looks like that's a supported type of expansion
>;
};
static_assert(std::is_same<
AsLoudNest1<Egg<int>, Egg<double>>::type,
LoudNest<Chick<int>, Chick<double>>
>::value, "Expected AsLoudNest1 to convert my Egg<T>s to Chick<T>s");
And try number two:
template <
class E, // does this need to be template<typename> class E?
typename... Rest>
struct AsLoudNest2 {
using type = LoudNest<
// Pretty sure the beginning is right.
AsChick<E>::type,
// This line feels wrong, AsLoudNest2<...>::type is a concrete type, not a parameter pack
AsLoudNest2<Rest...>::type...
>;
};
// also, feels like I need a base case for AsLoudNest2?
My actual problem has to do with implementing an interpreter, and the classes are FormalParameter<T> (Egg<T>), ActualParameter<T> (Chick<T>), etc. However, I wanted to avoid using the word "parameter" for in the example code, as we're already talking about Parameter Packs in a different sense.
code from this post: https://godbolt.org/z/XBIEhm
I was able to fix your example with a few changes: https://godbolt.org/z/3VW68f
Add a deduction guide to LoudNest to alleviate the problem deducing the Chicks... types in LoudNest{hatchlings} (this probably isn't the only solution, but seemed clean in that it didn't require complicating the hatch() implementation):
template<typename... Chicks>
LoudNest(const std::tuple<Chicks...>& chicks) -> LoudNest<Chicks...>;
(Add hatchOne which was present in your question but not the godbolt link you shared)
Get rid of hatch_impl in favor of just calling hatchOne during pack expansion:
auto hatchlings = std::apply(
[&](auto... args) { return std::make_tuple(hatchOne(args)...); },
eggs);
Use a specialization to deduce the inner T types of the Egg parameters to AsLoudNest1:
template<typename... Eggs>
struct AsLoudNest1 {};
template<typename... Ts>
struct AsLoudNest1<Egg<Ts>...> {
using type = LoudNest<Chick<Ts>...>;
};

Extracting a tuple of value_type from a tuple of containers in C++11

I have a function with a template parameter which I know to be a std::tuple of several standard C++ containers of varying element types.
How can I extract, out of this, a type that is a std::tuple of the element types?
For example, suppose I have the following function
template <typename TupOfCtrs>
void doStuff(const TupOfCtrs& tupOfCtrs) {
using TupOfElements = /*extract a tuple type by applying CtrT::value_type to each container in tupOfCtrs and combining the results into an std::tuple*/;
MyHelperClass<TupOfElements> helper;
}
and I know it is being called like this:
std::list<Foo> l {/*...*/};
std::vector<Bar> v {/*...*/};
std::deque<Baz> d {/*...*/};
auto tup = std::make_tuple(l, v, d);
In this case, I want the TupOfElements helper type to be defined as std::tuple<Foo, Bar, Baz>.
Note that I do not need to actually create the tuple, only to get its type.
How can this be achieved, possibly using the Boost::Fusion library?
You can do this even in a more simple manner without Boost Fusion like this:
// Template which takes one type argument:
template <typename Tuple> struct TupOfValueTypes;
// Only provide a definition for this template for std::tuple arguments:
// (i.e. the domain of this template metafunction is any std::tuple)
template <typename ... Ts>
struct TupOfValueTypes<std::tuple<Ts...> > {
// This definition is only valid, if all types in the tuple have a
// value_type type member, i.e. the metafunction returns a type only
// if all types of the members in the std::tuple have a value_type
// type member, and a std::tuple can be constructed from these:
using type = std::tuple<typename Ts::value_type...>;
};
template <typename TupOfCtrs>
void doStuff(const TupOfCtrs& tupOfCtrs) {
using TupOfElements = typename TupOfValueTypes<TupOfCtrs>::type;
// ...
}
But it is of course easier to specify doStuff for the std::tuple explicitly:
template <typename ... Ts>
void doStuff(const std::tuple<Ts...> & tupOfCtrs) {
using TupOfElements = std::tuple<typename Ts::value_type...>;
// ...
}
PS: Also note, that in many cases if you need to just have a list of types, the std::tuple class is an overkill, and might slightly hurt compilation times. Personally, I've always instead used a simple TypeList struct:
template <typename ... Ts> struct TypeList
{ using type = TypeList<Ts...>; };
If you want doStuff to take a std::tuple, make that explicit:
template <class... Ts>
void doStuff(std::tuple<Ts...> const& tupOfCtr) { ... }
Once you have that parameter pack, it's just a matter of pulling out the value_type:
template <class... Ts>
void doStuff(std::tuple<Ts...> const& tupOfCtr)
{
using value_tuple = std::tuple<typename Ts::value_type...>;
// ...
}

Variadic template unrolling to std::tuple

I have a filter class that takes two template parameters, the number of inputs and the number of outputs.
template<int Ins, int Outs>
class Filter
{
// implementation
};
Sometimes I need to chain multiple filters in series so I was thinking to wrap them in a class
template<int... args>
class Chain
{
};
so that when I use the chain
Chain<5, 10, 25, 15> chain;
it unrolls the args into a tuple to end up with something like this in the Chain class
std::tuple<Filter<5, 10>, Fiter<10, 25>, Filter<25, 15>> filters;
Is something like this possible? I'm fairly new to these concepts and can't wrap my head around it.
We can do this in three lines and no recursion:
template<int... args>
struct Chain
{
// put args... into a constexpr array for indexing
static constexpr int my_args[] = {args...};
// undefined helper function that computes the desired type in the return type
// For Is... = 0, 1, ..., N-2, Filter<my_args[Is], my_args[Is+1]>...
// expands to Filter<my_args[0], my_args[1]>,
// Filter<my_args[1], my_args[2]>, ...,
// Filter<my_args[N-2], my_args[N-1]>
template<size_t... Is>
static std::tuple<Filter<my_args[Is], my_args[Is+1]>...>
helper(std::index_sequence<Is...>);
// and the result
using tuple_type = decltype(helper(std::make_index_sequence<sizeof...(args) - 1>()));
};
Demo.
We can do this with some recursive template magic:
//helper class template which will handle the recursion
template <int... Args>
struct FiltersFor;
//a helper to get the type of concatenating two tuples
template <typename Tuple1, typename Tuple2>
using tuple_cat_t = decltype(std::tuple_cat(std::declval<Tuple1>(),
std::declval<Tuple2>()));
//pop off two ints from the pack, recurse
template <int Ins, int Outs, int... Others>
struct FiltersFor<Ins,Outs,Others...>
{
//the type of concatenating a tuple of Filter<Ins,Outs> with the tuple from recursion
using type = tuple_cat_t<std::tuple<Filter<Ins,Outs>>,
typename FiltersFor<Outs,Others...>::type>;
};
//base case, 1 int left
template <int Dummy>
struct FiltersFor<Dummy>
{
using type = std::tuple<>;
};
//for completeness
template <>
struct FiltersFor<>
{
using type = std::tuple<>;
};
//our front-end struct
template<int... args>
using Chain = typename FiltersFor<args...>::type;
Alternatively, we can get rid of the single int and no int versions and define the primary template like this:
template <int... Args>
struct FiltersFor
{
using type = std::tuple<>;
};
Now we can test this like so:
static_assert(std::is_same<Chain<1,2,3,4>, std::tuple<Filter<1,2>,Filter<2,3>,Filter<3,4>>>::value, "wat");
static_assert(std::is_same<Chain<1,2>, std::tuple<Filter<1,2>>>::value, "wat");
static_assert(std::is_same<Chain<>, std::tuple<>>::value, "wat");
Demo
I already faced a similar problem and ended up with an operator* for class Filter tha accepts a Filter<Ins1, Ins> object and builds a Filter<Ins1, Outs>.
template<int Ins, int Outs>
class Filter
{
template <int Ins1>
Filter<Ins1, Outs> operator*(const Filter<Ins1, Ins> &rhs) const
{
// implementation
}
};
Now the question is: what your filter does? Is composition possible (maybe I am biased from the fact that in my context Filter was a function and in my case operator* was function composition)