Sign in to follow this  

Check if template parameter exists in variadic template?

This topic is 1187 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

Basically, I want this to work:

struct Foo { /*stuff*/ };
struct Bar { /*other stuff*/ };
struct Tar { /* other other stuff */ };
 
template <class... T>
struct Attributes
{};
 
struct MyClass
{
    /* magic */
};
 
struct Container
{
    template <class T>
    MyClass* add(MyClass* myClass, T&& attributes)
    {
        myClass->setAttributes(attributes);
        /* store pointer in internal std::vector so it can be deleted by Container */
        return myClass;
    }
};
int main() {
     MyClass* myClass = Container.add(new MyClass, Attributes<Foo, Bar>());
     std::cout << myClass->hasAttribute<Foo>() << std::endl;
     std::cout << myClass->hasAttribute<Bar>() << std::endl;
     std::cout << myClass->hasAttribute<Tar>() << std::endl;
     return 0;
}

 
 
The output should be:

1 (true)
1 (true)
0 (false)

I am completely stuck on how I'm supposed to achieve this.

Edited by TheComet

Share this post


Link to post
Share on other sites

There are a lot of issues you're going to have to deal with here...

  1. Storing the Attributes<> in your MyClass will require you to store it via pointer to a base type, unless you template MyClass based on the attributes.
  2. Your queries will all be compile time, and the types will only get their attributes set at compile time. This will make it difficult for the code to adapt to runtime conditions, i.e. data driven. As I don't know what the purpose of this code is, I cannot tell you if this is actually a problem or not, but you should be aware of it.
  3. Testing for attributes, assuming a non-attribute templated MyClass is...complex at best. I've attached a sample implementation below, although if I spent the time I could probably come up with something a lot cleaner and friendlier.
#include <iostream>
#include <memory>
#include <tuple>

struct Foo { /*stuff*/ };
struct Bar { /*other stuff*/ };
struct Tar { /* other other stuff */ };

template <class... T>
struct Attributes
{
};

namespace detail {
	struct attribute_wrapper_base { virtual ~attribute_wrapper_base() {} };

	template<typename T>
	struct attribute_wrapper : attribute_wrapper_base {
	};

	template<std::size_t I, class T>
	struct attribute_tuple_for {
		static bool is_attribute(attribute_wrapper_base const& b) {
			if (dynamic_cast<attribute_wrapper<typename std::tuple_element<I, T>::type> const*>(&b))
				return true;

			return attribute_tuple_for<I - 1, T>::is_attribute(b);
		}
	};

	template<class T>
	struct attribute_tuple_for<-1, T> {
		static bool is_attribute(attribute_wrapper_base const& b) {
			return false;
		}
	};

	struct attributes_container_base {
		virtual bool has_attribute(attribute_wrapper_base const& b) {
			return false;
		}
	};

	template<typename... T>
	struct attributes_container : attributes_container_base {
		typedef std::tuple<T...> tuple_type;

		attributes_container(Attributes<T...>&& a) {
			attributes = std::move(a);
		}

		bool has_attribute(attribute_wrapper_base const& b) override {
			return attribute_tuple_for<std::tuple_size<tuple_type>::value - 1, tuple_type>::is_attribute(b);
		}


		Attributes<T...> attributes;
	};
}
struct MyClass
{
	std::unique_ptr<detail::attributes_container_base> attributes;

	template<class T>
	bool hasAttribute() { 
		return attributes->has_attribute(detail::attribute_wrapper<T>());
	}

	template<class... T>
	void setAttributes(Attributes<T...>&& a) {
		attributes.reset(new detail::attributes_container<T...>(std::move(a)));
	}
};

struct Container
{
	template<class... T>
	std::unique_ptr<MyClass>&& add(std::unique_ptr<MyClass>&& myClass, Attributes<T...>&& attributes)
	{
		myClass->setAttributes(std::move(attributes));
		/* store pointer in internal std::vector so it can be deleted by Container */
		return std::move(myClass);
	}
};

int main() {
	Container container;
	std::unique_ptr<MyClass> myClass(container.add(std::unique_ptr<MyClass>(new MyClass), Attributes<Foo, Bar>()));
	std::cout << myClass->hasAttribute<Foo>() << std::endl;
	std::cout << myClass->hasAttribute<Bar>() << std::endl;
	std::cout << myClass->hasAttribute<Tar>() << std::endl;
	return 0;
}

Share this post


Link to post
Share on other sites

Thanks a lot, that was very helpful.

 

You were right, I ran into way too many problems further down the road. My solution ended up being to make Attributes<...>() a function instead of a class that converts and returns a std::set<std::type_info*> of the passed template parameters. That gave me the runtime requirements I needed.

 

Thanks again!

Share this post


Link to post
Share on other sites

This topic is 1187 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