Is it possible to have lookup table of classes in C++? - c++

I have a function:
template <class T, class Array>
void DumpArrayDebug(Array& out, const T& source)
It is supposed to be used to dump data from array classes in Maya (Autodesk app) into regular vector.
Example of such array types:
MFloatArray;
MColorArray;
MIntArray;
These classes have the same interface, yet they have no base class.
Currently I use this function in a following way:
MFloatArray someInternalMayaFloatData;
...
std::vector<float> buffer;
DumpArrayDebug(buffer, someInternalMayaFloatData);
Looking at this code makes me wonder is it possible to somehow tie 2 classes inside the template through something like lookup table?
So that result should look something like this:
template <class T>
void dumpArrayDbg(const T& source, ClassLookupTable<T>& out)
Thus far I was able to come up with the following monstrosity:
template <typename T>
struct ClassLookupTable
{
T classname;
};
template <>
struct ClassLookupTable<MIntArray>
{
std::vector<int> classname;
};
template <>
struct ClassLookupTable<MFloatArray>
{
std::vector<float> classname;
};
template <>
struct ClassLookupTable<MColorArray>
{
std::vector<MColor> classname;
};
template <class T>
void dumpArrayDbg(const T& source, decltype(ClassLookupTable<T>::classname)& out)
{
int length = source.length();
out.clear();
out.resize(length);
source.get(out.data());
}
Is there a more elegant solution?

This is a standard template metaprogramming technique: a traits type. The only things I would change are using standard template metaprogramming idioms (type is the standard name for a type trait, not classname) and avoid having the trait specify vector:
template <typename T>
struct MayaArrayBaseElementTrait; //The primary template should never be used.
template <>
struct MayaArrayBaseElementTrait<MIntArray>
{
using type = int;
};
template <>
struct MayaArrayBaseElementTrait<MFloatArray>
{
using type = float;
};
template <>
struct MayaArrayBaseElementTrait<MColorArray>
{
using type = MColor;
};
template<typename T>
using MayaArrayBaseElementTrait_t = typename MayaArrayBaseElementTrait<T>::type;
template <class T>
void dumpArrayDbg(const T& source, std::vector<MayaArrayBaseElementTrait_t<T>>& out)
{
int length = source.length();
out.clear();
out.resize(length);
source.get(out.data());
}
This way, the mapping is from the Maya array type to the base element type. This gives you the freedom to create mappings to other types besides vector. You could create a std::array or std::list or whatever you like. Also, if you ever want to change the allocator type for the vector, you are free to do so, unlike your original code.

Is there a more elegant solution?
I propose the following...
Given a simple template type list container
template <typename ...>
struct typeList
{ };
and a recursive template as follows
template <typename, typename>
struct lookForType
{ };
template <typename T, typename V, typename ... Ts>
struct lookForType<T, typeList<T, V, Ts...>>
{ using type = V; };
template <typename T, typename U, typename V, typename ... Ts>
struct lookForType<T, typeList<U, V, Ts...>>
: lookForType<T, typeList<Ts...>>
{ };
with an helper using to simplify the extraction of type
template <typename T, typename L>
using lookForType_t = typename lookForType<T, L>::type;
you can create the mapping as follows
using myList = typeList<MIntArray, std::vector<int>,
MFloatArray, std::vector<float>,
MColorArray, std::vector<Color>>;
and get the required type using lookForType or lookForType_t
using l1 = lookForType_t<MIntArray, myList>;
The following is a full compiling example
#include <vector>
#include <type_traits>
template <typename ...>
struct typeList
{ };
template <typename, typename>
struct lookForType
{ };
template <typename T, typename V, typename ... Ts>
struct lookForType<T, typeList<T, V, Ts...>>
{ using type = V; };
template <typename T, typename U, typename V, typename ... Ts>
struct lookForType<T, typeList<U, V, Ts...>>
: lookForType<T, typeList<Ts...>>
{ };
template <typename T, typename L>
using lookForType_t = typename lookForType<T, L>::type;
struct Color {};
struct MFloatArray {};
struct MColorArray {};
struct MIntArray {};
int main()
{
using myList = typeList<MIntArray, std::vector<int>,
MFloatArray, std::vector<float>,
MColorArray, std::vector<Color>>;
using l1 = lookForType_t<MIntArray, myList>;
using l2 = lookForType_t<MFloatArray, myList>;
using l3 = lookForType_t<MColorArray, myList>;
static_assert( std::is_same_v<std::vector<int>, l1>, "!" );
static_assert( std::is_same_v<std::vector<float>, l2>, "!" );
static_assert( std::is_same_v<std::vector<Color>, l3>, "!" );
}

Related

Typelist of nested types

I've got a typelist providing the following interface :
template <typename... Ts>
struct type_list
{
static constexpr size_t length = sizeof...(Ts);
template <typename T>
using push_front = type_list<T, Ts...>;
template <typename T>
using push_back = type_list<Ts..., T>;
// hidden implementation of complex "methods"
template <uint64_t index>
using at;
struct pop_front;
template <typename U>
using concat;
template <uint64_t index>
struct split;
template <uint64_t index, typename T>
using insert;
template <uint64_t index>
using remove;
};
In another piece of code, I have such a typelist TL of types statically inheriting a base class providing such an interface :
template<typename Derived>
struct Expression {
using type1 = typename Derived::_type1;
using type2 = typename Derived::_type2;
};
struct Exp1 : Expression<Exp1> {
template<typename> friend struct Expression;
private:
using _type1 = float;
using _type2 = int;
};
struct Exp2 : Expression<Exp2> {
template<typename> friend struct Expression;
private:
using _type1 = double;
using _type2 = short;
};
I want to make the typelist of nested types from TL, something like :
using TL = type_list<Exp1, Exp2>;
using TL2 = type_list<TL::type1...>; // type_list<float, double>
but I can't expand TL as it's not an unexpanded parameter pack.
I've thought about index_sequence but can't manage to make it work.
The question is seemingly looking for map, also called transform in C++. TL is one list of types, and the desire is to apply some type-level function (extract ::type1) and have another list of types. Writing transform is straightforward:
template <template <typename> typename fn, typename TL>
struct type_list_transform_impl;
template <template <typename> typename fn, typename... Ts>
struct type_list_transform_impl<fn, type_list<Ts...>>
{
using type = type_list<fn<Ts>...>;
};
template <template <typename> typename fn, typename TL>
using type_list_transform = type_list_transform_impl<fn, TL>::type;
And then the type-level function:
template <typename T>
using type1_of = typename T::type1;
And combine the pieces to get TL2:
using TL2 = type_list_transform<type1_of, TL>; // type_list<float, double>
Example: https://godbolt.org/z/b7TMoac5c

Grouping same type variadic template types into <type, int> variadic types, omitting <type, 0> from the variadic list

I'm implementing compile time unit system, I am able to multiply different units together such that, for example:
Scalar<int, M_>{2} * Scalar<int, M_>{2} == Scalar<int, M_, M_>{4};
I want also to be able to do this:
Scalar<int, M_, M_>{4} / Scalar<int, M_>{2} == Scalar<int, M_>{2};
And I think a good place to start would be to group similar units to a template< typename T, int P> struct UnitPower type, so that
Scalar<int, UnitPower<M_, 1>>{2} * Scalar<int, UnitPower<M_, 1>>{2} == Scalar<int, UnitPower<M_, 2>>{4};
and
Scalar<int, UnitPower<M_, 2>>{4} / Scalar<int, UnitPower<M_, 1>>{2} == Scalar<int, UnitPower<M_, 1>>{2};
This will come in handy for a more general case:
Scalar<double, UnitPower<M_, 1>, UnitPower<S_, -2>, UnitPower<G_, 1>>{70} / Scalar<double, UnitPower<M_, 1>, UnitPower<S_, -1>>{10} == Scalar<double, UnitPower<S_, -1>, UnitPower<G_, 1>>{7}
I would also need to make the operators agnostic to the order of these UnitPowers...
Here's the code so far:
struct M_;
struct S_;
struct Mps_;
template<typename T, int P>
struct UnitPower {};
template<typename T, int P, typename... R>
struct group_units {
//static constexpr type = ?
};
template <typename T, class... C>
struct Scalar
{
protected:
T value;
public:
constexpr explicit Scalar(const T value) : value(value) {}
template<typename U>
constexpr auto operator<=>(const Scalar<U, C...> rhs) {
return value <=> static_cast<U>(rhs.value);
}
template<typename U>
constexpr bool operator==(const Scalar<U, C...> rhs) const { return value == static_cast<T>(rhs); }
template<typename U, typename... D>
constexpr Scalar<std::common_type_t<T, U>, C..., D...> operator/(const Scalar<U, D...> rhs) const {
using V = std::common_type_t<T, U>;
return Scalar<V, C..., D...>{static_cast<V>(value) / static_cast<V>(rhs)};
}
template<typename U, typename... D>
constexpr Scalar<std::common_type_t<T, U>, C..., D...> operator*(const Scalar<U, D...> rhs) const {
using V = std::common_type_t<T, U>;
return Scalar<V, C..., D...>{static_cast<V>(value) * static_cast<V>(rhs)};
}
template<typename U>
constexpr std::common_type_t<T, U> operator/(const Scalar<U, C...> rhs) const {
using V = std::common_type_t<T, U>;
return static_cast<V>(value) / static_cast<V>(rhs);
}
template<typename U>
constexpr Scalar<std::common_type_t<T, U>, C...> operator/(const U rhs) const {
using V = std::common_type_t<T, U>;
return Scalar<V, C...>{static_cast<V>(value) / static_cast<V>(rhs)};
}
constexpr explicit operator T() const { return value; }
template<typename U>
constexpr explicit operator U() const { return static_cast<U>(value); }
};
template<typename T>
struct Meters : Scalar<T, M_> { using Scalar<T, M_>::Scalar; };
template<typename T>
struct Seconds : Scalar<T, S_> { using Scalar<T, S_>::Scalar; };
As you can see, the division operator is currently only defined for same unit (in the same order), and returns just a number (which is the correct return type in this case), or takes just a number, returning the Scalar with no unit modification, which is also the correct behavior.
The multiplication operator just appends the units, and I need it to group them first.
I've added the
template<int P, typename T>
struct UnitPower {};
template<int P, typename T, typename... R>
struct group_units {
//static constexpr type = ?
};
Part, but I don't really know how to go about it..
I'm also not sure how to make the operators unit order agnostic.
After learning how to group the units for the multiplication operator, the division operator would be similar to the multiplication with regards to units - just using negative powers for the right hand side.
So my question is two-fold:
How to make the function unit order agnostic?
How to group similar units into a UnitPower<U, int> structs, and omit units whose power is 0? (and decay Scalar to the underlying value type if all UnitPowers are omitted).
Pretty challenging... Even though I only did some basic tests below code seems to work, at very least it should give you quite a number of hints how to solve the problem. There's yet pretty much potential for beautifying the code (even though got less with latest edit) and naming of my templates sure is anything else but optimal – but I leave that to you to fix... As a little compensation division is already supplied, too ;)
The idea is always based on the same fundamental principle: We need to recurse into the template arguments to make the type changes we need. Keeping that in mind it should be possible to understand the code below. If questions remain, feel free to leave a comment.
template <typename Unit, int>
struct UnitPower { };
template <typename T, typename ... Units>
struct Scalar
{
T value;
};
template <typename ... Units>
struct concat;
template <typename ... Units>
using concat_t = typename concat<Units...>::type;
template <typename U, typename ... Units>
struct concat<U, std::tuple<Units...>>
{
using type = std::tuple<U, Units...>;
};
template <typename ... UnitsX, typename ... UnitsY>
struct concat<std::tuple<UnitsX...>, std::tuple<UnitsY...>>
{
using type = std::tuple<UnitsX..., UnitsY...>;
};
template <typename ... Units>
struct powers;
template <typename ... Units>
using powers_t = typename powers<Units...>::type;
template <>
struct powers<>
{
using type = std::tuple<>;
};
template <typename U, typename ... Units>
struct powers<U, Units...>
{
using type = concat_t<UnitPower<U, 1>, powers_t<Units...>>;
};
template <typename U, int N, typename ... Units>
struct powers<UnitPower<U, N>, Units...>
{
using type = concat_t<UnitPower<U, N>, powers_t<Units...>>;
};
template <typename ... Units>
struct count;
template <typename ... Units>
using count_t = typename count<Units...>::type;
template <typename U, int P>
struct count<UnitPower<U, P>>
{
using type = UnitPower<U, P>;
};
template <typename U, int PX, int PY, typename ... Units>
struct count<UnitPower<U, PX>, UnitPower<U, PY>, Units...>
{
using type = count_t<UnitPower<U, PX + PY>, Units...>;
};
template <typename UX, int PX, typename UY, int PY, typename ... Units>
struct count<UnitPower<UX, PX>, UnitPower<UY, PY>, Units...>
{
using type = count_t<UnitPower<UX, PX>, Units...>;
};
template < typename ... Units>
struct count<std::tuple<Units...>>
{
using type = count_t<Units...>;
};
template <typename ... Units>
struct remain;
template <typename ... Units>
using remain_t = typename remain<Units...>::type;
template <typename U, int P>
struct remain<UnitPower<U, P>>
{
using type = std::tuple<>;
};
template <typename U, int PX, int PY, typename ... Units>
struct remain<UnitPower<U, PX>, UnitPower<U, PY>, Units...>
{
using type = remain_t<UnitPower<U, PX>, Units...>;
};
template <typename UX, int PX, typename UY, int PY, typename ... Units>
struct remain<UnitPower<UX, PX>, UnitPower<UY, PY>, Units...>
{
using type = concat_t<
UnitPower<UY, PY>,
remain_t<UnitPower<UX, PX>, Units...>
>;
};
template < typename ... Units>
struct remain<std::tuple<Units...>>
{
using type = remain_t<Units...>;
};
template <typename ... Units>
struct combine;
template <typename ... Units>
using combine_t = typename combine<Units...>::type;
template <>
struct combine<>
{
using type = std::tuple<>;
};
template <typename U, int P>
struct combine<UnitPower<U, P>>
{
using type = std::tuple<UnitPower<U, P>>;
};
template <typename U, int P, typename ... Units>
struct combine<UnitPower<U, P>, Units...>
{
using type = concat_t<
count_t<UnitPower<U, P>, Units...>,
combine_t<remain_t<UnitPower<U, P>, Units...>>
>;
};
template <typename ... Units>
struct combine<std::tuple<Units...>>
{
using type = combine_t<Units...>;
};
template <typename ... Units>
struct normalize;
template <typename ... Units>
using normalize_t = typename normalize<Units...>::type;
template <>
struct normalize<>
{
using type = std::tuple<>;
};
template <typename U, typename ... Units>
struct normalize<UnitPower<U, 0>, Units...>
{
using type = normalize_t<Units...>;
};
template <typename U, typename ... Units>
struct normalize<UnitPower<U, 1>, Units...>
{
using type = concat_t<U, normalize_t<Units...>>;
};
template <typename U, int N, typename ... Units>
struct normalize<UnitPower<U, N>, Units...>
{
using type = concat_t<UnitPower<U, N>, normalize_t<Units...>>;
};
template <typename ... Units>
struct normalize<std::tuple<Units...>>
{
using type = normalize_t<Units...>;
};
template <typename T, typename ... Units>
struct scalar;
template <typename ... Units>
using scalar_t = typename scalar<Units...>::type;
template <typename T, typename ... Units>
struct scalar<T, std::tuple<Units...>>
{
using type = Scalar<T, Units...>;
};
template <typename ... T>
struct multiply;
template <typename ... T>
using multiply_t = typename multiply<T...>::type;
template <typename TX, typename TY, typename ... UnitsX, typename ... UnitsY>
struct multiply<TX, TY, std::tuple<UnitsX...>, std::tuple<UnitsY...>>
{
using type = scalar_t<
decltype(std::declval<TX>() * std::declval<TY>()),
normalize_t<combine_t<concat_t<
powers_t<UnitsX...>, powers_t<UnitsY...>
>>>
>;
};
template <typename TX, typename TY, typename ... UnitsX, typename ... UnitsY>
auto operator*(Scalar<TX, UnitsX...> x, Scalar<TY, UnitsY...> y)
-> multiply_t<TX, TY, std::tuple<UnitsX...>, std::tuple<UnitsY...>>
{
return {x.value * y.value};
}
template <typename ... Units>
struct negate;
template <typename ... Units>
using negate_t = typename negate<Units...>::type;
template <>
struct negate<>
{
using type = std::tuple<>;
};
template <typename U, int N, typename ... Units>
struct negate<UnitPower<U, N>, Units...>
{
using type = concat_t<UnitPower<U, -N>, negate_t<Units...>>;
};
template <typename ... Units>
struct negate<std::tuple<Units...>>
{
using type = negate_t<Units...>;
};
template <typename ... T>
struct divide;
template <typename ... T>
using divide_t = typename divide<T...>::type;
template <typename TX, typename TY, typename ... UnitsX, typename ... UnitsY>
struct divide<TX, TY, std::tuple<UnitsX...>, std::tuple<UnitsY...>>
{
using type = scalar_t<
decltype(std::declval<TX>() / std::declval<TY>()),
normalize_t<combine_t<concat_t<
powers_t<UnitsX...>, negate_t<powers_t<UnitsY...>>
>>>
>;
};
template <typename TX, typename TY, typename ... UnitsX, typename ... UnitsY>
auto operator/(Scalar<TX, UnitsX...> x, Scalar<TY, UnitsY...> y)
-> divide_t<TX, TY, std::tuple<UnitsX...>, std::tuple<UnitsY...>>
{
return {x.value / y.value};
}
Here's what I came up with.
#include <tuple>
#include <type_traits>
template <typename T, typename Tuple>
struct remove_from_tuple;
template <typename T, typename Tuple>
using remove_from_tuple_t = typename remove_from_tuple<T, Tuple>::type;
template <typename T, typename... ElemT>
struct remove_from_tuple<T, std::tuple<ElemT...>> {
using type = decltype(std::tuple_cat(std::declval<
std::conditional_t<std::is_same_v<T, ElemT>, std::tuple<>, std::tuple<ElemT>>
>()...));
static constexpr std::size_t removed =
sizeof...(ElemT) - std::tuple_size<type>::value;
};
template <class Tuple1, class Tuple2>
struct is_tuple_permutation : std::false_type {};
template <class Tuple1, class Tuple2>
constexpr bool is_tuple_permutation_v = is_tuple_permutation<Tuple1, Tuple2>::value;
template <>
struct is_tuple_permutation<std::tuple<>, std::tuple<>>
: public std::true_type {};
template <typename T, typename... List1, typename Tuple2>
struct is_tuple_permutation<std::tuple<T, List1...>, Tuple2> {
private:
using remove1_t = remove_from_tuple<T, std::tuple<List1...>>;
using remove2_t = remove_from_tuple<T, Tuple2>;
public:
static constexpr bool value =
1 + remove1_t::removed == remove2_t::removed &&
is_tuple_permutation_v<typename remove1_t::type, typename remove2_t::type>;
};
struct M_;
struct S_;
struct KG_;
template <class Tag, int P>
struct UnitPower {};
// Trait: UnitPower<Tag0, P0>, UnitPower<Tag1, P1>, ... -> Tag0
template <class... Units>
struct first_tag;
template <class... Units>
using first_tag_t = typename first_tag<Units...>::type;
template <class Tag0, int P0, class... Units>
struct first_tag<UnitPower<Tag0, P0>, Units...> {
using type = Tag0;
};
// Trait: Sum powers of all UnitPower with matching Tag in Units...;
// Put all Units... with different Tag in tuple remainder.
template <class Tag, class... Units>
struct collect_unit;
template <class Tag, class... Units>
constexpr int collect_unit_power = collect_unit<Tag, Units...>::power;
template <class Tag, class... Units>
using collect_unit_remainder_t = typename collect_unit<Tag, Units...>::remainder;
template <class Tag>
struct collect_unit<Tag> {
static constexpr int power = 0;
using remainder = std::tuple<>;
};
template <class Tag, int P0, class... Units>
struct collect_unit<Tag, UnitPower<Tag, P0>, Units...> {
static constexpr int power = P0 + collect_unit_power<Tag, Units...>;
using remainder = collect_unit_remainder_t<Tag, Units...>;
};
template <class Tag, class Unit0, class... Units>
struct collect_unit<Tag, Unit0, Units...> {
static constexpr int power = collect_unit_power<Tag, Units...>;
using remainder = decltype(std::tuple_cat(
std::declval<std::tuple<Unit0>>(),
std::declval<collect_unit_remainder_t<Tag, Units...>>()));
};
// Trait: Combine any units with the same Tag.
template <class Tuple>
struct group_units;
template <class Tuple>
using group_units_t = typename group_units<Tuple>::type;
template <>
struct group_units<std::tuple<>> {
using type = std::tuple<>;
};
template <class... Units>
struct group_units<std::tuple<Units...>> {
private:
using Tag0 = first_tag_t<Units...>;
using collect_t = collect_unit<Tag0, Units...>;
public:
using type = decltype(std::tuple_cat(
std::declval<std::conditional_t<
collect_t::power != 0,
std::tuple<UnitPower<Tag0, collect_t::power>>,
std::tuple<>>>(),
std::declval<group_units_t<typename collect_t::remainder>>()));
};
template <typename T, class... Units>
class Scalar;
// Trait: Do two Scalars have the same underlying type and the same units
// in any order?
template <class S1, class S2>
struct Scalars_compatible : public std::false_type {};
template <class S1, class S2>
constexpr bool Scalars_compatible_v = Scalars_compatible<S1, S2>::value;
template <typename T1, class... Units1, typename T2, class... Units2>
struct Scalars_compatible<Scalar<T1, Units1...>, Scalar<T2, Units2...>>
: public std::bool_constant<is_tuple_permutation_v<
std::tuple<Units1...>, std::tuple<Units2...>>>
{};
template <typename T, class Tuple>
struct tuple_to_Scalar;
template <typename T, class Tuple>
using tuple_to_Scalar_t = typename tuple_to_Scalar<T, Tuple>::type;
template <typename T, class... Units>
struct tuple_to_Scalar<T, std::tuple<Units...>> {
using type = Scalar<T, Units...>;
};
template <class S1, class S2>
struct Scalar_product;
template <class S1, class S2>
using Scalar_product_t = typename Scalar_product<S1, S2>::type;
template <typename T1, class... Units1, typename T2, class... Units2>
struct Scalar_product<Scalar<T1, Units1...>, Scalar<T2, Units2...>> {
using type = tuple_to_Scalar_t<
std::common_type_t<T1, T2>,
group_units_t<std::tuple<Units1..., Units2...>>>;
};
template <class Unit>
struct invert_unit;
template <class Unit>
using invert_unit_t = typename invert_unit<Unit>::type;
template <class Tag, int P>
struct invert_unit<UnitPower<Tag, P>> {
using type = UnitPower<Tag, -P>;
};
template <class S1, class S2>
struct Scalar_quotient;
template <class S1, class S2>
using Scalar_quotient_t = typename Scalar_quotient<S1, S2>::type;
template <typename T1, class... Units1, typename T2, class... Units2>
struct Scalar_quotient<Scalar<T1, Units1...>, Scalar<T2, Units2...>> {
using type = tuple_to_Scalar_t<
std::common_type_t<T1, T2>,
group_units_t<std::tuple<Units1..., invert_unit_t<Units2>...>>>;
};
using Distance_t = Scalar<double, UnitPower<M_, 1>>;
using Time_t = Scalar<double, UnitPower<S_, 1>>;
using Speed_t = Scalar_quotient_t<Distance_t, Time_t>;
using Acceleration_t = Scalar_quotient_t<Speed_t, Time_t>;
using Mass_t = Scalar<double, UnitPower<KG_, 1>>;
using Energy_t = Scalar_product_t<Mass_t, Acceleration_t>;
static_assert(Scalars_compatible_v<Energy_t, Scalar<double, UnitPower<KG_, 1>, UnitPower<M_, 1>, UnitPower<S_, -2>>>);
static_assert(Scalars_compatible_v<Scalar_quotient_t<Speed_t, Acceleration_t>, Time_t>);
Note you'll probably want to restrict your operator+, operator==, etc. to require Scalars_compatible_v.
See it on godbolt.
The answer to template metaprogramming questions is to always use Boost.Mp11.
So first, you probably don't want to support both Scalar<int, M, M> and Scalar<int, Power<M, 2>>, the former has limited value anyway. But I'll start motivating Mp11 by demonstrating how to convert just a list of powers (some of which may be Powers) into a normalized set (not yet grouped):
// using a type (that is an integral constant)
// for power rather than an integer, for convenience
template <typename Unit, typename Power>
struct UnitPower { };
// if T is a list (it would be a UnitPower)
// otherwise it's a base unit with power 1
template <typename T>
using normalize_unit = mp_if<
mp_is_list<T>, T, UnitPower<T, mp_int<1>>>;
template <typename L>
using normalize = mp_transform<normalize_unit, L>;
I could've written this to take a pack of T... and just produce an mp_list there but I wanted to start using algorithms earlier. So here, normalize<mp_list<M, M, UnitPower<M, mp_int<2>>> yields the type mp_list<UnitPower<M, mp_int<1>>, UnitPower<M, mp_int<1>>, UnitPower<M, mp_int<2>>>
Alright, so now that we have a bunch of UnitPowers, we can group them. The way to do this is to keep a map of Unit to Power. A map in the Mp11 sense is just a "list" of "pairs", where each key only appears once. But UnitPower is a "pair" in the Mp11 sense, and the grouping algorithm we want is basically the example for mp_map_update:
template <typename M, typename T>
using update_powers = mp_map_update_q<M, T,
mp_bind<mp_plus, _2, mp_second<T>>>;
template <typename L>
using grouped_powers = mp_fold<normalize<L>, mp_list<>, update_powers>;
And... that's it actually. The slightly awkward function there is because mp_map_update takes a function that gets called with the unit and then the power of the element already in the map, so we need to pull out its power (_2) and add it (mp_plus) to the power we're currently updating (mp_second<T>).
Then, with that, multiplication is grouping both lists together:
template <typename L1, typename L2>
using multiply_powers = grouped_powers<mp_append<L1, L2>>;
And dividing is the same but we have to negate the right-hand list first:
template <typename UP>
using negate = mp_replace_second<UP, mp_int<-mp_second<UP>::value>>;
template <typename L1, typename L2>
using divide_powers = multiply_powers<L1, mp_transform<negate, normalize<L2>>>;
And that's all we need. Demo.
static_assert(std::same_as<
multiply_powers<mp_list<M, M>, mp_list<M, S>>,
mp_list<UnitPower<M, mp_int<3>>, UnitPower<S, mp_int<1>>>>);
static_assert(std::same_as<
divide_powers<mp_list<M, M>, mp_list<M, S>>,
mp_list<UnitPower<M, mp_int<1>>, UnitPower<S, mp_int<-1>>>>);

Compare templates itselves and not instantiated template-types

My goal is to be able to compare templates i.e. write something like this
template<typename T>
struct template_type;
template<template <typename...> typename TTmpl, typename ...Ts>
struct template_type<TTmpl<Ts...>> { using type = TTmpl; }; // [1] this isn't valid C++
template<typename TRng, typename T>
auto find(TRng&& rng, const T& val)
{
using TTmpl = typename mcu::template_type<std::remove_const_t<std::remove_reference_t<TRng>>>::type;
if constexpr (std::is_same_v<TTmpl, std::set>) // [2] this isn't valid C++
{
return rng.find(val);
}
else
{
using namespace std;
return std::find(begin(rng), end(rng), val);
}
}
My second attemp was to use alias template and custom same function. Something like:
template<typename T>
struct template_type;
template<template <typename...> typename TTmpl, typename ...Ts>
struct template_type<TTmpl<Ts...>> { template<typename ...Us> using type = TTmpl<Us...>; };
template<template<typename...>typename T1, template<typename...> typename T2>
struct same : std::false_type {};
template<template<typename...>typename T>
struct same<T, T> : std::true_type {};
But this doesn't work either. The problem is that same<std::set, template_type<std::set<int>>::type>::value returns false, i.e. template class and it's template alias are different for some reason.
My third attemp was to write something like template_type_identity but I cannot figure out what should I use as identity:
template<template<typename...> typename T>
struct template_type_identity { using type = ???; }; // I could hardcode some constexpr ids for each template but I wanna generic solution
I am not sure if I understand what you want, also because I would use a different solution for your motivating case (sfinae on the presence of member find). Anyhow, this is how you can check if a given type is an instantiation of a template, provided the template has only type parameters:
#include <iostream>
#include <type_traits>
#include <set>
#include <vector>
template <template<typename...> typename T, typename C>
struct is_instantiation_of : std::false_type {};
template <template<typename...> typename T,typename ...P>
struct is_instantiation_of< T,T<P...>> : std::true_type {};
int main(){
std::cout << is_instantiation_of<std::set,std::set<int>>::value;
std::cout << is_instantiation_of<std::set,std::vector<int>>::value;
}
Output:
10

Parameterize of tuple with repeated type

I want to declare template:
template <size_t N, class Type> my_tuple
{
using type = ... //something here
};
So that this my_typle<3, std::string>::type,for example, will be the same as this std::tuple<std::string, std::string, std::string>
Please, show, how it can be done in one line, maybe, using std::index_sequence or something from boost or whatever? Or maybe it can not be done just simple?
UPD
Please, note I do not need an std::array, I need to parametrize some variable template with predefined list of types. I have used std::tuple as an example here.
This is fun. Here's a "pure" meta-programming approach to expand the sequence:
template<typename T, typename Seq>
struct expander;
template<typename T, std::size_t... Is>
struct expander<T, std::index_sequence<Is...>> {
template<typename E, std::size_t>
using elem = E;
using type = std::tuple<elem<T, Is>...>;
};
template <size_t N, class Type>
struct my_tuple
{
using type = typename expander<Type, std::make_index_sequence<N>>::type;
};
I say "pure" ironically. It's more akin to the classic meta-programming tricks, nothing else.
Use decltype with return type of function returning tuple<string,string,...repeated N times>:
template<typename T, size_t ... Indices>
auto GetType(std::index_sequence<Indices...>) {
return std::make_tuple( (Indices,T{})... );
}
template <size_t N, class Type> class my_tuple
{
using type = decltype(GetType<Type>(std::make_index_sequence<N>())); //something here
};
template<class T>
struct Dummy;
int main(){
Dummy<my_tuple<3,std::string>::type> d;
error on d gives you tuple<string,string,string>, so it is your desired type.
You can
template <typename...>
struct cat_tuple_type;
template <typename... T1, typename... T2>
struct cat_tuple_type<std::tuple<T1...>, std::tuple<T2...>>
{
using type = std::tuple<T1..., T2...>;
};
template <size_t N, class Type> struct my_tuple
{
static_assert(N>0);
using type = typename cat_tuple_type<std::tuple<Type>, typename my_tuple<N-1, Type>::type>::type;
};
template <class Type> struct my_tuple <1, Type>
{
using type = std::tuple<Type>;
};
Then my_typle<3, std::string>::type gives the type std::tuple<std::string, std::string, std::string>.

std::tuple of std::shared_ptr of template parameter pack

I want to implement a class template that:
behaves like a function
it's input and output variables are all shared.
relatively easy to use.
As a result, I construct the following:
// all input/output variable's base class
class basic_logic_parameter;
// input/output variable, has theire value and iterators to functions that reference to this variable
template <typename FuncIterator, typename ValueType>
class logic_parameter
:public basic_logic_parameter
{
private:
std::list<FuncIterator> _refedFuncs;
ValueType _val;
public:
};
// all `function`'s base class
class basic_logic_function
{
public:
virtual ~basic_logic_function() = 0;
};
// the function, has input/output variable
template <typename FuncIterator, typename R, typename... Args>
class logic_function_base
:public basic_logic_function
{
private:
std::shared_ptr<logic_parameter<FuncIterator, R>> _ret;
std::tuple<std::shared_ptr<logic_parameter<FuncIterator, Args>>...> _args;
public:
template <std::size_t N>
decltype(auto) arg()
{
return std::get<N>(_args);
}
template <std::size_t N>
struct arg_type
{
typedef std::tuple_element_t<N> type;
};
template <std::size_t N>
using arg_type_t = arg_type<N>::type;
decltype(auto) ret()
{
return _ret;
}
};
I wish to use as these like:
// drawing need color and a pen
struct Color
{
};
struct Pen
{
};
struct Iter
{
};
class Drawer
:public logic_function_base<Iter, void(Color, Pen)>
{
public:
void draw()
{
arg_type_t<0> pColor; // wrong
}
}
My compiler can not pass this code through, why? I just want convert a template parameter pack to std::tuple of std::shared_ptr of them.
for example:
Given struct A, int, struct C, I want to have:
std::tuple<
std::shared_ptr<logic_parameter<A>>,
std::shared_ptr<logic_parameter<int>>,
std::shared_ptr<logic_parameter<C>>,
>
The problem (once the small errors are fixed1) is that you instantiate:
logic_function_base<Iter, void(Color, Pen)>
...meaning that FuncIterator is Iter and R is void(Color, Pen), so Args is emtpy <>, so decltype(_args) is an empty std::tuple<>, and your code fails to obtain the type of the 0th element of an empty tuple, which is legit.
What you want is partial specialization of logic_function_base:
template <typename F, typename T>
class logic_function_base;
template <typename FuncIterator, typename R, typename... Args>
class logic_function_base<FuncIterator, R(Args...)>: public basic_logic_function {
};
1 Small mistakes in your current code:
template <std::size_t N>
struct arg_type
{
typedef std::tuple_element_t<N, decltype(_args)> type; // Missing the tuple type
};
template <std::size_t N>
using arg_type_t = typename arg_type<N>::type; // Missing a typename
This may not answer your whole question, but you could use the following trait to wrap tuple element types.
template <typename T> struct wrap;
template <typename... T>
struct wrap<std::tuple<T...>> {
using type = std::tuple<std::shared_ptr<logic_parameter<T>>...>;
}
template <typename T>
using wrap_t = typename wrap<T>::type;
You can then use it like this:
std::tuple<int,double,char> t1;
wrap_t<decltype(t)> t2;
The type of t2 is std::tuple<std::shared_ptr<logic_parameter<int>>,std::shared_ptr<logic_parameter<double>>,std::shared_ptr<logic_parameter<char>>>.