Sign in to follow this  

Match template type in variadic templates

This topic is 398 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello,

 

I've got the following (simplified test) case:

template<typename ... Args>
class Test
{
     using Tuple = std::tuple<Args...>;

     template<typename Type>
     ReturnType<Type> Get(void) // ReturnType<> is a custom type, see explanation below
     {
          constexpr auto ID = sys::getTupleIndex<Tuple, Type>();
     }

private:

     Tuple m_data;
};

To give a context, I'm using this to generate a cache-pack for components in my entity/component system, used like that:

entities.EntititesWithComponent<Transform, Mesh>(); // returns a cached structure of all component-packs with Transform+Mesh

Now for the actual question, its also possible to give modifiers to those component types, like Optional (which is just an empty struct) for a component that is not required:

entities.EntitiesWithComponents<Transform, Mesh, Optional<Script>>(); // all component-packs of Transform+Mesh, if a Script is attached it will be cached as well

Now look at my Get-Implementation. Obviously I can just make a GetOptional-explicit overload, or pass in Get<Optional<Script>>, but I'd prefer to just be able to Call Get<Script>, and it will resolve depending on whether I specified Optional<> in the cache-declaration or not.

 

The actual difference is that Get Should return Type& for non-optional, and Type* for optional types.

 

So what I need is some sort of lookup from the tuple like I do with my sys::tupleIndex, only that it looksup the actual complete type for a stripped type... (I hope everyone follows, feels kind of hard to explain). Kind of like this:

template<typename Type>
ReturnType<lookupRealType<Tuple, Type>> Get(void)
{
    // Tuple = std::tuple<Transform, Mesh, Optional<Script>
    using RealType = lookupRealType<Tuple, Type>; // RealType should be Optional<Script> when Script is passed in as Type
}

Can't wrap my head around a way to do it, confusing template shit is confusing. Anyone got some ideas how to achieve this?

Edited by Juliean

Share this post


Link to post
Share on other sites

I'd use something like this:

// ----- Optional -----
template<typename T> struct Optional {
	T data;
	};


// ----- HasOptional -----
template<typename S, typename... T> struct HasOptional {
	static const bool value = HasOptional<S>::value || HasOptional<T...>::value;
	};

template<typename T> struct HasOptional<T> {
	static const bool value = false;	
	};

template<typename T> struct HasOptional<Optional<T>> {
	static const bool value = true;
	};


// ----- Func -----
template<typename R, typename... T>
	typename std::enable_if<!HasOptional<T...>::value, R&>::type Func() {
	cout << "no optional type" << endl;
	return * new R();			// ya I know its an error...
	}

template<typename R, typename... T>
	typename std::enable_if<HasOptional<T...>::value, R*>::type Func() {
	cout << "has optional type" << endl;
	return nullptr;
	}


// ----- main -----
void main() {

	typedef int T0;
	typedef Optional<int> T1;
	typedef Optional<float> T2;

	cout << "T0 = " << HasOptional<T0, int, float>::value << endl;
	cout << "T1 = " << HasOptional<T1, T2, T0, int>::value << endl;

	Func<int, T0, int, float>();
	Func<int, T1, T2, T0, int>();

	getchar();
	} 

 
To be honest I'm not exactly sure whether you'd want the return type templated or not, there are so many different forms of ECS.  I don't think you actually need to use std::tuple in there, well at least from what you're posting it seems spurious.  Course that may also be the fact that I haven't slept in a while...

Share this post


Link to post
Share on other sites

Thats not quite what I wanted, because I don't want to explicitely typedef Optional<int> as T1, I just want it to be naturally matched like:

Cache<Optional<int>, float>::IsOptional<int>::value // value should be true for int and false for float

I think I found a possible solution:

template<typename Component, typename ... Components>
struct RealTypeHelper
{
private:
	using Tuple = std::tuple<Components...>;
	using RealTuple = std::tuple<StripOptional<Components...>::value>; // this DOESN'T work 
public:

	using value = typename std::tuple_element<sys::tupleIndex<Component, RealTuple>(), Tuple>::type;
};

template<typename Component, typename ... Components>
using RealType = typename RealTypeHelper<Component, Components...>::value;

If I can generate a tuple of <int, float> alongside one of <Optional<int>, float>, than I can resolve the real type of any component.

The only problem is generating this array. I have the "StripOptional" template which will remove the Optional<>-tag, but how can I apply this to all elements of an variadic template pack?

template<typename Component>
struct StripOptional
{
    using value = Component;
};

template<typename Component>
struct StripOptional<Optional<Component>>
{
    using value = Component;
};

using RealTuple = std::tuple<StripOptional<Components...>::value>; // this DOESN'T work 

How would this line work?

 

EDIT:

I did use a std::array instead of a tuple, but now I need to store elements with different/no base classes, so tuple is actually the cleanest solution (and requires no casting). Also the tuple was always required/the easiest way of accessing the type-index in the variadic parameter pack.

Edited by Juliean

Share this post


Link to post
Share on other sites

HasOptional above doesn't require any special typedef's, I just used one to save on typing because it was late and I was being lazy :)
For example: HasOptional<float, int, Optional<char>>::value == true, and you can define Optional however you want, the template magic is all in HasOptional.
 
If you want to strip the Optional to use later (say in defing a tuple) use this:

// ----- StripOptional -----
template<typename T> struct StripOptional {
	typedef T Type;
	};

template<typename T> struct StripOptional<Optional<T>> {
	typedef T Type;
	};

If you wanted to wrap it up into its own class use this (you were close, just the ... needed to be in another spot, and don't forget your typename's, though the compiler will fortunately tell you when they are missing):

// ----- RealTupleType -----
template<typename... T> struct RealTupleType {
	typedef std::tuple< typename StripOptional<T>::Type... > Type;	
	};

An example for its use would be:

// ----- TupleExample -----
template<typename... T> void TupleExample() {
	typedef tuple<T...> OTT;
	typedef typename RealTupleType<T...>::Type RTT;
	cout << "original tuple type = " << typeid(OTT).name() << endl;
	cout << "real tuple type = " << typeid(RTT).name() << endl;
	}


// ----- main -----
// new main function assuming previous code and new code are in the same file
void main() {

	typedef int T0;
	typedef Optional<int> T1;     // the typedef's aren't necessary, they just save me from typing
	typedef Optional<float> T2;

	cout << "T0 = " << HasOptional<T0, int, float>::value << endl;
	cout << "T1 = " << HasOptional<T1, T2, T0, int>::value << endl;

	Func<int, T0, int, float>();
	Func<int, T1, T2, T0, int>();

	cout << "StripOptional<T0> = " << typeid(typename StripOptional<T0>::Type).name() << endl;
	cout << "StripOptional<T1> = " << typeid(typename StripOptional<T1>::Type).name() << endl;
	cout << "StripOptional<T2> = " << typeid(typename StripOptional<T2>::Type).name() << endl;

	TupleExample<T0, T1, T2>();

	getchar();
	}
Edited by Ryan_001

Share this post


Link to post
Share on other sites
and don't forget your typename's, though the compiler will fortunately tell you when they are missing):

 

Ahh jesus, thanks, thats what was missing. I almost had what you are showing here, I just forgot to add the typename and thus my compiler gave me unclear error. Seems solved so far, thanks :)

template<typename Component, typename ... Components>
struct RealTypeHelper
{
private:
	using Tuple = std::tuple<Components...>;
	using RealTuple = std::tuple<typename StripOptional<Components>::value...>; // missed the typename here
public:

	using value = typename std::tuple_element<sys::tupleIndex<Component, RealTuple>(), Tuple>::type;
};

template<typename Component, typename ... Components>
using RealType = typename RealTypeHelper<Component, Components...>::value; 

RealType<int, Optional<int>, float>::value => Optional<int>
Edited by Juliean

Share this post


Link to post
Share on other sites

This topic is 398 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this