I want to write a function type that transform a type B in the same type but with the same sign that another type A. I can achieve that using this function:
template <typename A, typename B, bool = std::is_signed_v<B>>
struct same_sign_as{
using type = std::make_signed_t<A>;
};
template <typename A, typename B>
struct same_sign_as<A, B, false>{
using type = std::make_unsigned_t<A>;
};
and then use it as using R = same_sign_as_t<A,B>;.
But I want my code to be readable. When I see a function simliar to same_sign_as I never know if it means the type A with the sign of B or the type of B with the sign of A.
As I don't want to remember this kind of things I'd like to write better something similar to
using R = make_type<A>::same_sign_as<B>
With this I'm not going to make any mistakes.
Is easy to write it:
template <typename A>
struct make_type{
template <typename B>
struct same_sign_as
{ using type = typename __same_sign_as<A, B>::type; };
};
where I changed the name of same_sign_as for __same_sign_as (I want this function to be an implementation detail).
To use make_type write:
using Res = typename make_type<A>::template same_sign_as<B>::type
and that is horrible. You can't read that!!!
Can I define an alias to achieve my goal of writing
using Res = make_type<A>::same_sign_as<B>
?
Related
Consider a alias template declaration, depending on a single template parameter that can have a finite number of values, like a class enum.
I would like to a employ using to define a type alias for every value of the class enum.
One way of implementing is to use std::conditional:
class enum color { white, red, blue };
struct A {};
struct B {};
struct C {};
template <color c>
using data = std::conditional_t<c == color::white, A, std::conditional_t<c == color::red, B, C>>;
Obviously, when the class enum color is expanded to new values, one needs an additional nested std::conditional, which is rather cumbersome.
I am looking for a solution to express this in a "scalable" way, that is, such that on expanding the class enum one has to do a minimal work.
Something like
template <class c>
using data = some_struct<c, A, B, C>;
where some_struct "select" the type A, B, C depending on the first parameter c.
How can I implement that (in a scalable way)?
One way you can do this is to create a tuple of the types to pick from and match those up with the order of the enum you have. Then you can just get the type from the tuple at the index of the value of the enumeration. That would look like
using type_set = std::tuple<A, B, C>;
template <color c>
using data_t = typename std::tuple_element<static_cast<size_t>(c), type_set>::type;
Now all you need to do is add types to the tuple that defines type_set as you add enumerations to color. You can see it working in this live example which uses an error message to tell you what data_t<color::red> resolves to the type B.
You can use specialization. Primary and helper:
template <color c> struct data;
template <color c> using data_t = data<T>::type;
Then for each case its one specialization to be written:
template <> struct data<color::white> { using type = A; }
template <> struct data<color::red> { using type = B; }
// ...
Adding a new enum mapped to a new type is then just adding a specialization
Given the following classes:
// Some random class
class A { };
// A templated class with a using value in it.
template<class TYPE_B>
class B {
public:
using TYPE = TYPE_B;
};
Next we use these two classes in class C. But if we are using B as the template parameter we would like to obtain the TYPE defined in it.
template<class TYPE_C>
class C {
// A check to see if we have a class of type B
static constexpr bool IS_B = std::is_same<B<int32_t>, TYPE_C>::value ||
std::is_same<B<int64_t>, TYPE_C>::value;
public:
// This is what not works. How to get B::TYPE here?
using TYPE = std::conditional<IS_B, TYPE_C::TYPE, TYPE_C>;
};
Class C would we used like:
C<A> ca;
C<B<int32_t>> cb32;
C<B<int64_t>> cb64;
I am compiling this in GCC. My fear what I would like not have to do is to use the std::is_same statement for each type used with B. Put that in the std::conditional. Are there any alternatives?
You have two issues in your code:
You are missing a typename before TYPE_C::TYPE. Since TYPE_C::TYPE is a dependent name, you need to use typename to tell the compiler that you are looking for a type.
You cannot use TYPE_C::TYPE ins the std::conditional1 expression because when TYPE_C is not B<>, that expression is invalid, but will still be evaluated.
One simple solution is to use an intermediate template to access the type, using template specialization, e.g.
template <class T>
struct get_C_type {
using type = T;
};
template <class T>
struct get_C_type<B<T>> {
using type = T;
};
template<class TYPE_C>
class C {
public:
// you still need a typename here
using TYPE = typename get_C_type<TYPE_C>::type;
};
1 You probably want to use std::conditional_t or std::conditional<>::type here.
I have a parameter pack given in a variadic template class and want to extract the first type.
Currently I do this, which works fine but is somehow cumbersome. Is it possible to do the same thing simpler? FirstEntityTypeshould be defined to have the type of the first type in EntityTs. Note, I want to keep the signature of the class template. I know that it would be possible to write template<typename FirstEntityType, typename... OtherEntityTypes>, it is however something that I don't want to do.
template<typename... EntityTs>
struct EntityContext
{
template<typename T, typename ... Ts>
struct K {
using type = T;
};
using FirstEntityType = typename K<EntityTs...>::type;
// ...
};
You could write:
using FirstEntityType = std::tuple_element_t<0, std::tuple<EntityTs...>>;
Or you could use Boost.Mp11:
using FirstEntityType = mp_front<EntityContext>;
You may use
std::tuple_element<0, std::tuple<EntityTs...>>::type
If I have two unrelated templates A and B, how do I perform a compiletime check to see if there are two identical parametrations. E.g. if I have some parameter type P1 and P2, I want to let the client to instantiate A<P1> and B<P2> but not A<P1> and B<P1>.
I have a solution to this using a variadic template Checker<typename... Xx> that one has to instatiate with the templates in question:
using a = A<P1>;
using b = B<P1>;
using checker = Checker<a, b>;
checker::init(); // can be empty
// static_assert fails here
This works if every parameter typ has a unique id (mask) and therefore I can OR all the masks and see if the number of ones is equal to the size of the parameter pack of Checker.
This solution is not good because a client can forget to instatiate the Checker template.
I wonder if this check can be made somewhat more under the hood?
This solution is not good because a client can forget to instatiate the Checker template.
Good intuition - you should therefore prevent the client from instantiating A and B before instantiating Checker. You can achieve this by providing them as type aliases accessible from Checker only.
namespace detail
{
template <typename T> struct A;
template <typename T> struct B;
}
template <typename P0, typename P1>
struct Checker
{
static_assert(!std::is_same_v<P0, P1>);
using A = detail::A<P0>;
using B = detail::B<P1>;
};
Usage:
using ClientABProvider = ABProvider<foo, bar>;
using A = typename ClientABProvider::A;
using B = typename ClientABProvider::B;
using ClientABProvider = ABProvider<foo, foo>; // Error!
using A = typename ClientABProvider::A;
using B = typename ClientABProvider::B;
If you want A and B to be user-provided, you can have Checker's type aliases be templates - but this kind of defeats its purpose since the user has already access to A and B in the first place.
template <typename P0, typename P1>
struct Checker
{
static_assert(!std::is_same_v<P0, P1>);
template <template <typename> class XA>
using A = XA<P0>;
template <template <typename> class XB>
using B = XB<P1>;
};
I have a formulation like this:
template <typename A, typename B>
struct SomeLibraryClass
{
using Foo = A;
using Bar = B;
};
Now I want to use it but rename it since conceptually the usage is different.
template <typename A, typename B>
using quaz = SomeLibraryClass<A, B>;
However, if I want to get bar, it's still quaz::bar. But say I want quaz::bar to be quaz::foobar instead. Is this possible? Why or why not?
I cannot use inheritance. It silently breaks the code.
You cannot add or remove members, or change the name of a member of a class that is already defined. All you can do is define a new class.