Klasa szablonowa zarządzana enumem w zależności od typu kontenera

0

Dla każdego kontenera osobno kod się kompiluje i działa prawidłowo.
Natomiast kod w całości nie chce działać. Wydaje mi się, że nieprawidłowo wykrywa Type,
gdy są oba kontenery. Nie wiem jak to ugryźć.

#include <iostream>
#include <set>
#include <map>

class ComponentType
{
public:
	enum class eType { map, vector, set, simple };
};

template <typename Type>
class Show
{
	const ComponentType::eType mode;
public:
	Show(ComponentType::eType m)
		:
		mode(m)
	{}

	void FillComponent(Type& comp)
	{
		if (mode == ComponentType::eType::map)
		{
			comp.insert({ 2, 6 });
			comp.insert({ 8, 6 });
		}
		else if (mode == ComponentType::eType::set)
		{
			comp.insert({ 7 });
			comp.insert({ 4 });
		}
	}

	void PrintComponent(const Type& comp)
	{
		if (mode == ComponentType::eType::map)
		{
			for (const auto& item : comp)
			{
				std::cout << item.first << " : " << item.second << '\n';
			}
			std::cout << '\n';
		}
		else if (mode == ComponentType::eType::set)
		{
			for (const auto& item : comp)
			{
				std::cout << item << ' ';
			}
			std::cout << '\n';
		}
	}

};

int main()
{
	using Map = std::map<int, int, std::less<int>>;
	using Set = std::set<int, std::less<int>>;

	Map m;
	Set s;

	Show<Map>  map(ComponentType::eType::map);
	map.FillComponent(m);
	Show<Set> set(ComponentType::eType::set);
	set.FillComponent(s);

	map.PrintComponent(m);
	set.PrintComponent(s);
} 
3

Nie da się tak prosto dynamicznie ustalać typu kontenera. Musisz wykorzystać specjalizację szablonów, inaczej kompilator będzie próbował skompilować kod, który nie ma prawa działać.

template<typename>
struct Inserter;

template<typename T>
struct InserterBase
{
	using container_type = T;
	InserterBase(container_type& c) : c(c) {}
	container_type& c;
};

template<typename T, typename Comp>
struct Inserter<std::set<T, Comp>> : InserterBase<std::set<T, Comp>>
{
	using InserterBase<std::set<T, Comp>>::InserterBase;

	void operator()(typename Inserter::container_type::value_type const& v) const {
		this->c.insert(v);
	}

};

template<typename T, typename V, typename Comp>
struct Inserter<std::map<T, V, Comp>> : InserterBase<std::map<T, V, Comp>>
{
	using InserterBase<std::map<T, V, Comp>>::InserterBase;

	void operator()(typename Inserter::container_type::value_type const& v) const {
		this->c.insert(v);
	}
};

template<typename T, typename Comp>
struct Inserter<std::vector<T, Comp>> : InserterBase<std::vector<T, Comp>>
{
	using InserterBase<std::vector<T, Comp>>::InserterBase;

	void operator()(typename Inserter::container_type::value_type const& v) const {
		this->c.push_back(v);
	}
};

template<typename T>
auto make_inserter(T& t){
	return Inserter<T>(t);
}

I wykorzystanie:

auto main() -> int
{
	vector<int> vi = {1,2,3};
	set<double> sd = {4,5,6};
	map<int, string> mis = {{42, "answer"}};

	auto vi_inserter = make_inserter(vi);
	auto sd_inserter = make_inserter(sd);
	auto mis_inserter = make_inserter(mis);

	vi_inserter(10);
	sd_inserter(5);
	sd_inserter(7);
	mis_inserter({69, "position"});

	DBG_CONT(vi);
	DBG_CONT(sd);
	for(auto const& p : mis){
		cout << "K: " << p.first << "\t V:" << p.second << endl;
	}
}

http://melpon.org/wandbox/permlink/2XUfgpi0F2Uv4Mgi

Ale coś mi się wydaje, że z tag dispatch można to ładniej zrobić.

============================================
Edit: tak, można:

struct unknown_type_please_specialize_trait{};
struct needs_insert{};
struct needs_push_back{};

template<typename T>
struct inserter_type { using type = unknown_type_please_specialize_trait; };

template<typename T, typename Comp, typename Alloc>
struct inserter_type<std::set<T, Comp, Alloc>> { using type = needs_insert; };

template<typename K, typename V, typename Comp, typename Alloc>
struct inserter_type<std::map<K, V, Comp, Alloc>> { using type = needs_insert; };

template<typename T, typename Alloc>
struct inserter_type<std::vector<T, Alloc>> { using type = needs_push_back; };

template<typename C, typename VT>
void my_insert(needs_insert, C& c, VT&& v){
	c.insert(std::forward<VT>(v));
}

template<typename C, typename VT>
void my_insert(needs_push_back, C& c, VT&& v){
	c.push_back(std::forward<VT>(v));
}

template<typename C>
void my_insert(C& c, typename decay_t<C>::value_type v){
	using tag = typename inserter_type<std::decay_t<C>>::type;
	my_insert(tag{}, c, std::move(v));
}

i main jest prostszy (ale insertera też se możesz zrobić oczywiście):

auto main() -> int
{
	vector<int> vi = {1,2,3};
	set<double> sd = {4,5,6};
	map<int, string> mis = {{42, "answer"}};

	my_insert(vi, 10);
	my_insert(sd, 5);
	my_insert(sd, 7);
	my_insert(mis, {69, "position"});

	DBG_CONT(vi);
	DBG_CONT(sd);
	for(auto const& p : mis){
		cout << "K: " << p.first << "\t V: " << p.second << endl;
	}
}

http://melpon.org/wandbox/permlink/ZOhaq2NydzPjJIXG

0
template<typename T>
struct InserterBase
{
    using container_type = T; // zdefiniowanie nazwy container_type w InserterBase::
    InserterBase(container_type& c) : c(c) {}
    container_type& c;
}; 
void operator()(typename Inserter::container_type::value_type const& v) const {// tutaj container_type jest w Inserter:: 

Czy to powinno być tak jak w przykładzie, czy poniżej?

void operator()(typename InserterBase::container_type::value_type const& v) const {
0
DBG_CONT(vi);
DBG_CONT(sd); 

@kq - czy mogę zamienić te makra na pętle w main(), czy mają one jakieś specjalne znaczenie?
Nie korzystałem z makr, bo podobno się od nich odchodzi.

1

Możesz pozamieniać na pętle, te makra to tylko ułatwienie dla mnie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1