Pull to refresh

Comments 11

Здорово, но я могу то же самое без буста и в 30 строк (проверка правда не уместилась :)

#include <string>
#include <iostream>

#define static_type(name) static constexpr const char* type_name() { return name; } 

struct disabled { static_type("disabled"); };
struct deferred { static_type("deferred"); };
struct deadline { static_type("deadline"); };

template<typename Expected, typename...T> 
struct match { typedef disabled type; };

template <typename Expected, typename...T>
struct match<Expected, Expected, T...> { typedef Expected type; };

template <typename Expected, typename Actual, typename...T>
struct match<Expected, Actual, T...> { typedef typename match<Expected, T...>::type type; };


template<class T, typename... Plugins>
struct container
{
    typedef typename match<deferred, Plugins...>::type deferred_;
    typedef typename match<deadline, Plugins...>::type deadline_;
    
    static std::string type_name()
    {
    	return std::string("container<") + deferred_::type_name() + ", " + deadline_::type_name() + ">";
    }
};

int main()
{
	using namespace std;
	cout << container<int>::type_name() << std::endl;
   	cout << container<int, deadline>::type_name() << std::endl;
   	cout << container<int, deferred>::type_name() << std::endl;
   	cout << container<int, deferred, deadline>::type_name() << std::endl;
   	cout << container<int, deadline, deferred>::type_name() << std::endl;
}
Круто. Я не смог придумать как variadic templates использовать в этом случае. Но в текущий момент у меня все равно нет с++11x, так что мой вариант для меня все еще актуален. Хотя наверно уже через пол года выкину его на свалку.
Извините, но я немножко не понял концепцию. Обычно, ведь, каждый параметр шаблона, в таких случаях, отвечает за некоторое свойство(которое имеет ряд значений). Например, boost::transform_iterator:
template <class UnaryFunction,
          class Iterator,
          class Reference = use_default,
          class Value = use_default>
class transform_iterator

Reference отвечает за член класса reference и, выставляя Reference, мы управляем «свойством» reference и т.д. И порядок имеет значение — чтобы выставить reference, мы задаём его значение 3м аргументом. Как Вы и сказали:
Но мы прекрасно понимаем, что как только мы поменяли два параметр у нас получится совершенно не то чего мы ожидали

А чтобы всё работало в случае с произвольным порядком нужно иметь «договорённость»: это «свойство» (параметр шаблона) имеет ряд конкретных значений(типов): 1, 2, 3. Другое свойство имеет другой набор: 4, 5, 6. Причём значения не должны совпадать(т.е. типы должны не совпадать, кроме как «значения по-умолчанию»). Я правильно понимаю? А это не усложняет всё? Не ограничивает?

UPD: если я правильно понял, то как раз это Вы и написали:
Способ который я хочу показать не позволяет специализировать шаблон произвольным типом.
Да, как раз так. Этот способ имеет существунное ограниение, и применим только в узком ряде задач, коий у меня возник и я решил описать.
На самом деле это ограничение легко обходится, достаточно только заменить параметры с типов на шаблоны.

Тогда вместо
struct deferred 

получится
template <typename T>
struct deferred_tag { typedef T type; };

А использоваться это будет так:
cout << container<int, deferred_tag<deferred_type_2>, deadline_tag<deadline_type>>::type_name() << std::endl;


В итоге получается как-то так
Посмотреть код
#include <string>
#include <iostream>

#define static_type(name) static constexpr const char* type_name() { return name; } 

struct disabled_type { static_type("disabled"); };

struct deferred_type { static_type("deferred"); };
struct deferred_type_2 { static_type("deferred2!"); };

struct deadline_type { static_type("deadline"); };
struct deadline_type_2 { static_type("deadline2!"); };

template <typename T = deferred_type>
struct deferred_tag { typedef T type; };

template <typename T = deadline_type>
struct deadline_tag {typedef T type; };

template<template <class> class Expected, typename...T> 
struct match { typedef disabled_type type; };

template <typename V, template <class> class Expected, typename...T>
struct match<Expected, Expected<V>, T...> { typedef typename Expected<V>::type type; };

template <template <class> class Expected, typename Actual, typename...T>
struct match<Expected, Actual, T...> { typedef typename match<Expected, T...>::type type; };


template<class T, typename... Plugins>
struct container
{
    typedef typename match<deferred_tag, Plugins...>::type deferred_;
    typedef typename match<deadline_tag, Plugins...>::type deadline_;
    
    static std::string type_name()
    {
    	return std::string("container<") + deferred_::type_name() + ", " + deadline_::type_name() + ">";
    }
};

int main()
{
    using namespace std;
    cout << container<int>::type_name() << std::endl;
    cout << container<int, deadline_tag<deadline_type>>::type_name() << std::endl;
    cout << container<int, deferred_tag<deferred_type>>::type_name() << std::endl;
    cout << container<int, deferred_tag<deferred_type>, deadline_tag<deadline_type>>::type_name() << std::endl;
    cout << container<int, deadline_tag<deadline_type>, deferred_tag<deferred_type>>::type_name() << std::endl;

    cout << container<int, deadline_tag<deadline_type_2>, deferred_tag<deferred_type_2>>::type_name() << std::endl;
    cout << container<int, deadline_tag<deadline_type_2>>::type_name() << std::endl;
    cout << container<int, deferred_tag<deferred_type_2>, deadline_tag<>>::type_name() << std::endl;
}



Ну или делать в обратную сторону —

struct deferred_type
{
typedef deferred_tag tag;
}

И матчить по tag
Могу предложить способ с использованием старых добрых enum'ов. Хорош тем, что порядок действительно неважен: container<T, DEADLINE|DEFERRED> и container<T, DEFERRED|DEADLINE> дают один и тот же тип (в отличие от приведенного в статье подхода).
#include <iostream>

#define static_type(name) static constexpr const char* type_name() { return name; }

struct disabled { static_type("disabled"); };
struct deferred { static_type("deferred"); };
struct deadline { static_type("deadline"); };

enum {
    DEFERRED = 1,
    DEADLINE = 2,
    MAX_POWER_OF_TWO = 4
};

template<int V, class A, class B>
struct type_selector {
    typedef B type;
};

template<class A, class B>
struct type_selector<0, A, B> {
    typedef A type;
};

template<class T, int Plugins = 0>
struct container
{
    static_assert(Plugins >= 0 && Plugins < MAX_POWER_OF_TWO, "Invalid flags");

    typedef typename type_selector<Plugins & DEFERRED, disabled, deferred>::type
        deferred_type;
    typedef typename type_selector<Plugins & DEADLINE, disabled, deadline>::type
        deadline_type;

    static std::string type_name() {
    	return std::string("container<") + deferred_type::type_name()
             + ", " + deadline_type::type_name() + ">";
    }
};

int main() {
    using namespace std;
    cout << container<int>::type_name() << std::endl;
   	cout << container<int, DEADLINE>::type_name() << std::endl;
   	cout << container<int, DEFERRED>::type_name() << std::endl;
   	cout << container<int, DEFERRED | DEADLINE>::type_name() << std::endl;
   	//cout << container<int, 6>::type_name() << std::endl; //Ошибка компиляции
    return 0;
}
В голове крутилась какая то похожая мысль но она не успела созреть. Спасибо за пример.
Нашел пример как сделать именнованые шаблонные параметры, это собстно именно то чего хотелось достич в идеале
stackoverflow.com/questions/11251242/boost-parameter-named-template-argument-in-combination-with-crtp

Так как можно указывать парметры в произвольном порядке и числе, а так же нет привязки к конкретным типам, и спокойно можно использовать новые (определнные пользователем) без какой либо модификации базового кода.
Sign up to leave a comment.

Articles