Trait to detect non-inherited typedef - c++

Is there a way to detect, if struct has typedef which is not inherited?
Following code fails on C
#include <iostream>
struct A { };
struct B : public A { typedef A Base; };
struct C : public B {};
template<typename T>
struct to_void
{
typedef void type;
};
template <typename T, typename dummy = void>
struct has_base_typedef : std::false_type {};
template <typename T>
struct has_base_typedef<T, typename to_void<typename T::Base>::type> : std::true_type {};
int main()
{
std::cout << has_base_typedef<A>::value;
std::cout << has_base_typedef<B>::value;
std::cout << has_base_typedef<C>::value;
}
trait for C gives true since Base is inherited (private modifier doesn't help AFAIK)
My goal is to get the snippet to print 010
Small example to play with on ideone.

Basically, you want to block implicit propagation of the Base alias to the derived class, which I don't think is possible. The only way I can think of to achieve something close to what you want is the following:
struct C;
struct A {};
struct B : public A {
using Base = A;
using Derived = C;
};
struct C : public B { };
template<typename T>
struct to_void {
typedef void type;
};
template <typename T, typename dummy = void>
struct has_base_typedef : std::false_type {};
template <typename T>
struct has_base_typedef<T, std::enable_if_t<!std::is_same<typename T::Derived, T>::value,
typename to_void<typename T::Base>::type>> : std::true_type {};
Live Demo

Related

Can you "hop" between "linked classes" in C++ metaprogramming?

Suppose you have something like this:
template<class D>
class HasDef {
public:
typedef D Def;
};
class A : public HasDef<class B> {};
class B : public HasDef<class C> {};
class C {};
So it is like a "metaprogramming linked list", with type links, via the included typedef Def. Now I want to make a template "Leaf" that, when applied to A, follows the links to yield C:
void f() {
Leaf<A>::type v; // has type C
}
Is it even possible at all to do this? I've tried some methods with std::compare and similar, but none are valid code: everything seems to run into issues with either that C has no Def typedef, or else that the type Leaf<> itself is incomplete when the inner recursive "call" is made so it (or its internal type type) cannot be referenced.
FWIW, the reason I want this is for making a "hierarchical state machine" where that Def represents the default state for each state in the hierarchy, and something a bit more elaborate that this seems to provide a fairly neat and clean "user interface syntax" for it.
I don't really like f(...) in modern code, thus my version uses void_t from C++17:
#include <type_traits>
template<class D>
struct HasDef {
typedef D Def;
};
struct A : HasDef<class B> {};
struct B : HasDef<class C> {};
struct C {};
template <typename T, typename=void>
struct DefPresent : std::false_type{};
template <typename T>
struct DefPresent<T, std::void_t<typename T::Def>> : std::true_type{};
template<typename T, bool deeper = DefPresent<T>::value>
struct Leaf
{
using Type = typename Leaf<typename T::Def>::Type;
};
template<typename T>
struct Leaf<T, false >
{
typedef T Type;
};
static_assert(std::is_same<typename Leaf<C>::Type, C>::value, "C");
static_assert(std::is_same<typename Leaf<B>::Type, C>::value, "B");
static_assert(std::is_same<typename Leaf<A>::Type, C>::value, "A");
https://godbolt.org/z/5h5rfe81o
EDIT: just for completenes, 2 C++20 variants utilizing concepts. Tested on GCC 10
#include <type_traits>
#include <concepts>
template<class D>
struct HasDef {
typedef D Def;
};
struct A : HasDef<class B> {};
struct B : HasDef<class C> {};
struct C {};
template <typename T>
concept DefPresent = requires(T a)
{
typename T::Def;
};
template<typename T>
struct Leaf
{
using Type = T;
};
template<typename T>
requires DefPresent<T>
struct Leaf<T>
{
using Type = Leaf<typename T::Def>::Type;
};
static_assert(std::is_same_v<typename Leaf<C>::Type, C>, "C");
static_assert(std::is_same_v<typename Leaf<B>::Type, C>, "B");
static_assert(std::is_same_v<typename Leaf<A>::Type, C>, "A");
template<typename T>
struct Leaf2
{
template <typename M>
static M test(M&&);
template <DefPresent M>
static auto test(M&&) -> typename Leaf2<typename M::Def>::Type;
using Type = decltype(test(std::declval<T>()));
};
static_assert(std::is_same<typename Leaf2<C>::Type, C>::value, "C");
static_assert(std::is_same<typename Leaf2<B>::Type, C>::value, "B");
static_assert(std::is_same<typename Leaf2<A>::Type, C>::value, "A");
https://godbolt.org/z/vcqEaPrja
You can implement this with SFINAE to separate types that have the typedef from ones that don't when trying to follow them to their leaf. I used the trick from this SO answer here.
The first implementation for Leaf follows the typedef recursively, the second one just defines the type itself as there is no typedef to follow.
Also note I changed your classes to struct for default-public inheritance. Also I changed the order of their definitions for this to compile.
Compiler explorer
#include <type_traits>
namespace detail
{
template<typename T> struct contains_def {
template<typename U> static char (&test(typename U::Def const*))[1];
template<typename U> static char (&test(...))[2];
static const bool value = (sizeof(test<T>(0)) == 1);
};
template<typename T, bool has = contains_def<T>::value>
struct Leaf {
using type = typename Leaf<typename T::Def>::type;
};
template<typename T>
struct Leaf<T, false> {
using type = T;
};
}
template <typename T>
using Leaf = detail::Leaf<T>; // expose Leaf to the global scope
template <typename T>
using Leaf_t = typename Leaf<T>::type; // for convenience
template<typename T>
struct AddDef {
using Def = T;
};
struct C {};
struct B : AddDef<C> {};
struct A : AddDef<B> {};
static_assert(std::is_same_v<Leaf_t<A>, C>);
static_assert(std::is_same_v<Leaf_t<B>, C>);
static_assert(std::is_same_v<Leaf_t<C>, C>);
You could make it a type trait:
#include <utility> // declval
template<class L>
struct Leaf {
template<class M>
static L test(const M&); // match L's without Def
template<class M> // match L's with Def
static auto test(M&&) -> typename Leaf<typename M::Def>::type;
// find matching test() overload, prefer test(M&&):
using type = decltype( test(std::declval<L>()) );
};
template<class L> // bonus helper types
using Leaf_t = typename Leaf<L>::type;
Demo and template resolution # cppinsights

How to filter a variadic template pack by type derivation?

I have a template class receiving multiple types, each type received is subclass of one of two options.
I want to expand them differently depending the parent class identifying each of them. This is equivalent to implement "filter" over the variadic template parameters.
For example:
class A{};
class B{};
template<class... C>
struct F{
std::tuple<types_derived_by<A, C>...> fn(types_subclassing<B, C>...){}
};
The types_derived_by template function should produce a variadic template pack with all the types in C that are derived from A, or B.
For example:
struct DA : public A{};
struct DB : public B{};
int main(){
F<DA, DB> f;
//f has a member function: DA fn(DB);
}
I'm using C++11, but I'm ok to move to c++14 if necessary.
You may do something like:
template <template <typename> class Pred, typename TUPLE, typename Res = std::tuple<>>
struct Filter;
template <template <typename> class Pred, typename Res>
struct Filter<Pred, std::tuple<>, Res>
{
using type = Res;
};
template <template <typename> class Pred, typename T, typename ... Ts, typename ... TRes>
struct Filter<Pred, std::tuple<T, Ts...>, std::tuple<TRes...>> :
Filter<Pred,
std::tuple<Ts...>,
std::conditional_t<Pred<T>::value,
std::tuple<TRes..., T>,
std::tuple<TRes...>>>
{
};
and then:
class A {};
template <typename T>
using is_base_of_A = std::is_base_of<A, T>;
class B {};
struct DA : public A{};
struct DB : public B{};
struct DA1 : public A{};
static_assert(std::is_same<std::tuple<DA, DA1>,
Filter<is_base_of_A, std::tuple<DA, DB, DA1>>::type>::value,
"unexpected");
Demo
If you don't mind using tuples as return value and parameter this might be a solution for you:
template <typename Base, typename...T>
struct base_filter;
template <typename Base>
struct base_filter<Base>
{
using type = std::tuple<>;
};
template <typename Base, typename T1>
struct base_filter<Base, T1>
{
using type = typename std::conditional_t<std::is_base_of<Base, T1>::value, std::tuple<T1>, std::tuple<>>;
};
template <typename Base, typename T1, typename...T>
struct base_filter<Base, T1, T...>
{
using type = decltype(std::tuple_cat(base_filter<Base, T1>::type(), base_filter<Base, T...>::type()));
};
//###########################################################
class A {};
class B {};
template<class...C>
struct F {
typename base_filter<A, C...>::type fn(typename base_filter<B, C...>::type){}
};
struct DA : public A {};
struct DB : public B {};
struct DA1 : public A {};
struct DA2 : public A {};
struct DB1 : public B {};
struct DB2 : public B {};
int main() {
std::tuple<DB> b;
F<DA, DB> f1;
std::tuple<DA> a = f1.fn(b);
std::tuple<DB1, DB2> bb;
F<DB1, DA1, DB2, DA2> f2;
std::tuple<DA1, DA2> aa = f2.fn(bb);
}

Recursive type upcasting with templates

I work with containers.
If A is a container then container_traits<A>::reference_container must type an A.
If container RefContainer<C> is_base_of A, then container_traits<A>::reference_container must type a C.
The following code does this upcasting (or dereferencing, as I say).
The problem is that, even if reference = false compiler checks if C::reference_container exists as type, and fail to compile.
Any other approaches?
#include <iostream>
using namespace std;
template<typename C> struct RefContainer;
template<class C>
class container_traits
{
template <typename R>
static std::true_type ref_helper(const RefContainer<R>&);
static std::false_type ref_helper(...);
public:
constexpr static bool reference = decltype(ref_helper(std::declval<C>()))::value;
typedef typename std::conditional<reference, typename C::reference_container, C>::type reference_container;
};
template<typename C>
struct RefContainer : public C { typedef typename container_traits<C>::reference_container reference_container; };
struct Container1 {};
struct Container2 {};
template<typename C> struct D : public RefContainer<C> {};
struct E : public RefContainer<D<RefContainer<Container1>>> {};
int main()
{
container_traits<Container1>::reference_container e; // It is Container1
container_traits<RefContainer<Container1>>::reference_container f; // dereference to Container1
container_traits<D<Container2>>::reference_container // dereference to Container2
container_traits<E>::reference_container h; // dereference to Container1
return 0;
}
Just create helper class
template<class T, bool der>
struct hlp
{
typedef T type;
}
template<class T>
struct hlp<RefContainer<T>, true>
{
typedef T::reference_container type;
}

Remove reference with const references

For a parametric class C, I want to get always the "primitive" type irrespective of pointer, const or reference modifiers.
template<typename __T>
class C
{
public:
typedef std::some_magic_remove_all<__T>::type T;
}
int main()
{
C<some_type>::type a;
}
For example, for some_type equal to:
int&
int**
int*&
int const &&
int const * const
and so on
I want a is always of type int. How can I achieve it?
If you want to use the standard library more, you can do:
#include <type_traits>
template<class T, class U=
typename std::remove_cv<
typename std::remove_pointer<
typename std::remove_reference<
typename std::remove_extent<
T
>::type
>::type
>::type
>::type
> struct remove_all : remove_all<U> {};
template<class T> struct remove_all<T, T> { typedef T type; };
which removes stuff until that doesn't change the type anymore. With a more recent standard, this can be shortened to
template<class T, class U=
std::remove_cvref_t<
std::remove_pointer_t<
std::remove_extent_t<
T >>>>
struct remove_all : remove_all<U> {};
template<class T> struct remove_all<T, T> { typedef T type; };
template<class T> using remove_all_t = typename remove_all<T>::type;
template<class T> struct remove_all { typedef T type; };
template<class T> struct remove_all<T*> : remove_all<T> {};
template<class T> struct remove_all<T&> : remove_all<T> {};
template<class T> struct remove_all<T&&> : remove_all<T> {};
template<class T> struct remove_all<T const> : remove_all<T> {};
template<class T> struct remove_all<T volatile> : remove_all<T> {};
template<class T> struct remove_all<T const volatile> : remove_all<T> {};
//template<class T> struct remove_all<T[]> : remove_all<T> {};
//template<class T, int n> struct remove_all<T[n]> : remove_all<T> {};
I originally also stripped extents (arrays), but Johannes noticed that this causes ambiguities for const char[], and the question doesn't mention them. If we also want to strip arrays (see also ideas mentioned in the comments), the following doesn't complicate things too much:
#include <type_traits>
template<class U, class T = typename std::remove_cv<U>::type>
struct remove_all { typedef T type; };
template<class U, class T> struct remove_all<U,T*> : remove_all<T> {};
template<class U, class T> struct remove_all<U,T&> : remove_all<T> {};
template<class U, class T> struct remove_all<U,T&&> : remove_all<T> {};
template<class U, class T> struct remove_all<U,T[]> : remove_all<T> {};
template<class U, class T, int n> struct remove_all<U,T[n]> : remove_all<T> {};
or with a helper class but a single template parameter:
#include <type_traits>
template<class T> struct remove_all_impl { typedef T type; };
template<class T> using remove_all =
remove_all_impl<typename std::remove_cv<T>::type>;
template<class T> struct remove_all_impl<T*> : remove_all<T> {};
template<class T> struct remove_all_impl<T&> : remove_all<T> {};
template<class T> struct remove_all_impl<T&&> : remove_all<T> {};
template<class T> struct remove_all_impl<T[]> : remove_all<T> {};
template<class T, int n> struct remove_all_impl<T[n]> : remove_all<T> {};
It is normal if all the variants start looking about the same ;-)
Also you can use the remove_cvref_t function, it has been available since c++20
#include <iostream>
#include <type_traits>
int main()
{
std::cout << std::boolalpha
<< std::is_same_v<std::remove_cvref_t<int>, int> << '\n'
<< std::is_same_v<std::remove_cvref_t<int&>, int> << '\n'
<< std::is_same_v<std::remove_cvref_t<int&&>, int> << '\n'
<< std::is_same_v<std::remove_cvref_t<const int&>, int> << '\n'
<< std::is_same_v<std::remove_cvref_t<const int[2]>, int[2]> << '\n'
<< std::is_same_v<std::remove_cvref_t<const int(&)[2]>, int[2]> << '\n'
<< std::is_same_v<std::remove_cvref_t<int(int)>, int(int)> << '\n';
}

Is it possible to selectively define a type in a derived class

I have a class template which looks like this:
template <Base>
struct foo : Base
{
typedef int some_type;
};
and I have a base which looks like this:
struct some_base
{
typedef float some_type;
};
Now foo<some_base>::some_type will be int as the derived foo will hide the Base::some_type. What I would like to do, is that if Base::some_type is defined, use that else, define some_type locally in foo as 'int - so question is, is this possible?
I could invert the relationship and save myself some headache, however it's not very logical in the real application...
Anything is possible with a bit of template metaprogramming :)
Start by writing a metafunction that determines whether a type has a nested type called "some_type". Something like this:
template <typename T>
struct has_some_type
{
typedef char no; // type with sizeof == 1
typedef struct { char x[2]; } yes; // type with sizeof == 2
template <typename X, typename Y = typename X::some_type>
struct foo {};
template <typename X>
static yes test(foo<X>*);
template <typename X>
static no test(...);
static const bool value = (sizeof(test<T>(0)) == sizeof(yes));
};
Now you can do something like this in the derived class:
template <typename T, bool has_some_type>
struct get_some_type;
template <typename T>
struct get_some_type<T, true>
{
typedef typename T::some_type type;
};
template <typename T>
struct get_some_type<T, false>
{
typedef int type; // the default type
};
template <typename base>
class derived : base
{
typedef typename get_some_type<base, has_some_type<base>::value>::type some_type;
...
};
This should work:
struct sfinae_types
{
struct yes { char x; };
struct no { char x[2]; };
};
template<class T>
class has_some_type : sfinae_types
{
private:
template<class U>
static yes test(typename U::some_type *);
template<class U>
static no test(...);
public:
enum { value = (sizeof(yes) == sizeof(test<T>(0))) };
};
template<bool, class T, typename DT>
struct get_some_type
{
typedef DT type;
};
template<class T, typename DT>
struct get_some_type<true, T, DT>
{
typedef typename T::some_type type;
};
struct B1
{
};
struct B2
{
typedef float some_type;
};
template<typename T>
struct D : T
{
typedef typename get_some_type<has_some_type<T>::value, T, int>::type some_type;
};
#include<iostream>
#include<typeinfo>
int main()
{
std::cout << has_some_type<B1>::value << std::endl;
std::cout << typeid(D<B1>::some_type).name() << std::endl;
std::cout << has_some_type<B2>::value << std::endl;
std::cout << typeid(D<B2>::some_type).name() << std::endl;
return(0);
}
And is a slight variation of what HighCommander4 presented a few seconds above...
I guess boost::mpl may come handy here and provide some useful TMP expressions I hand crafted above.
Give struct foo an additional template argument that defaults to int:
template <Base, Typedef = int>
struct foo : Base
{
typedef Typedef some_type;
};
Then foo<some_base, some_base::some_type>::some_type is some_base::some_type.