LINUX.ORG.RU

C++, шаблон, упростить.


0

2

Класс занимается тем, что представляет мульти-типовое 6-разрядное число, где каждый разряд имеет свой тип. Крутит итераторы: когда младший разряд доходит до end(), он его сбрасывает на begin() и накручивает итератор следующего разряда. Когда доходит до того, что все трераторы равны своим end() - ам, перестаёт идти вперёд.

Проход вперёд реализован в методе increment().

Сея логика не обсуждается, она нужна. Предлагать упрощать архитектуру приложения, которому это понадобилось - не надо, это ведроедный коллайдер шестого порядка на квази-рулезах. Короче, такой функционал нужен.

Хочется упростить реализацию этой многомерной фигни. Есть способы?

template < typename T1, typename T2, typename T3, typename T4, typename T5, typename T6 > class MultiTypeCounter
{
public:
	MultiTypeCounter( const T1& _o1, const T2& _o2, const T3& _o3, const T4& _o4, const T5& _o5, const T6& _o6 )
	: o1( _o1 )
	, o2( _o2 )
	, o3( _o3 )
	, o4( _o4 )
	, o5( _o5 )
	, o6( _o6 )
	{
		it_t1 = o1.begin(); ite_t1 = o1.end();
		it_t2 = o2.begin(); ite_t2 = o2.end();
		it_t3 = o3.begin(); ite_t3 = o3.end();
		it_t4 = o4.begin(); ite_t4 = o4.end();
		it_t5 = o5.begin(); ite_t5 = o5.end();
		it_t6 = o6.begin(); ite_t6 = o6.end();
	}

	// return:
	// true on success
	// false if limit reached
	bool increment()
	{
		do
		{
			if ( it_t1 != ite_t1)
				++it_t1;
			if ( it_t1 != ite_t1 )
				break;
			it_t1 = o1.begin();

			if ( it_t2 != ite_t2)
				++it_t2;
			if ( it_t2 != ite_t2 )
				break;
			it_t2 = o2.begin();

			if ( it_t3 != ite_t3)
				++it_t3;
			if ( it_t3 != ite_t3 )
				break;
			it_t3 = o3.begin();

			if ( it_t4 != ite_t4)
				++it_t4;
			if ( it_t4 != ite_t4 )
				break;
			it_t4 = o4.begin();

			if ( it_t5 != ite_t5)
				++it_t5;
			if ( it_t5 != ite_t5 )
				break;
			it_t5 = o5.begin();

			if ( it_t6 != ite_t6)
				++it_t6;
			if ( it_t6 != ite_t6 )
				break;
			it_t6 = o6.begin();

			// Limit reached
			return false;

		} while ( 0 );
		return true;
	}

	typename T1::const_iterator &iter1() const { return it_t1; }
	typename T2::const_iterator &iter2() const { return it_t2; }
	typename T3::const_iterator &iter3() const { return it_t3; }
	typename T4::const_iterator &iter4() const { return it_t4; }
	typename T5::const_iterator &iter5() const { return it_t5; }
	typename T6::const_iterator &iter6() const { return it_t6; }

private:
	MultiTypeCounter(const MultiTypeCounter&);
	MultiTypeCounter& operator=(const MultiTypeCounter&);

	// const references
	const T1 &o1;
	const T2 &o2;
	const T3 &o3;
	const T4 &o4;
	const T5 &o5;
	const T6 &o6;

	typename T1::const_iterator it_t1, ite_t1;
	typename T2::const_iterator it_t2, ite_t2;
	typename T3::const_iterator it_t3, ite_t3;
	typename T4::const_iterator it_t4, ite_t4;
	typename T5::const_iterator it_t5, ite_t5;
	typename T6::const_iterator it_t6, ite_t6;
};

template <typename T1, typename T2>
class multi {
    ...
}


template<typename T1, typename T2>
multi<T1, T2> mk_multi (T1 v1, T2 v2) {return multi<T1, T2> (v1, v2);}


template multi <>
class multi <void> {...}

typedef multi <T1, 
    multi <T2, 
	...
	    multi <T6,
		multi <void>
	    >
	...
    >
>
big_one;

template <typename t1, ..., typename t6>
big_one mk_big_one (t1 v1, ...t6 v6) {
      mk_multi (v1, mk_multi (v2, ... (mk_multi (v6, multi<void> ())) ... ))
}
ival ★★
()
Последнее исправление: ival (всего исправлений: 2)

Я бы курнул в сторону Boost.Fusion

ratatosk
()

Эталон. Опровергает все известные шаблоны...

ziemin ★★
()

Я бы курил в сторону C++11, tuple и variadic_template'ов.

Gorthauer ★★★★★
()

Бери чистый си и не майся херней, а пиши код.

anonymous
()

Почему здесь еще не отписался анонимус-лиспер, который все нагенерировал лабы на лиспе? Здесь очень был бы уместен комментарий о нужности макросов.

cdshines ★★★★★
()
Ответ на: комментарий от cdshines

Здесь очень был бы уместен комментарий о нужности макросов.

темплейты и так макросы. Если просто надо уменьшить число строк, то можно что-то типа

#define def(x) \
public: \
typename T##x::const_iterator &iter##x() const { return it_t##x; } \
private: \
const T##x &o##x; \
typename T##x::const_iterator it_t##x, ite_t##x;

#define process(x) \
if ( it_t##x != ite_t##x) \
 ++it_t##x; \
if ( it_t##x != ite_t##x ) \
 break; \
it_t##x = o##x.begin();

template < typename T1, typename T2, typename T3, typename T4, typename T5, typename T6 > class MultiTypeCounter
{
public:
	MultiTypeCounter( const T1& _o1, const T2& _o2, const T3& _o3, const T4& _o4, const T5& _o5, const T6& _o6 )
	: o1( _o1 )
	, o2( _o2 )
	, o3( _o3 )
	, o4( _o4 )
	, o5( _o5 )
	, o6( _o6 )
	{
		it_t1 = o1.begin(); ite_t1 = o1.end();
		it_t2 = o2.begin(); ite_t2 = o2.end();
		it_t3 = o3.begin(); ite_t3 = o3.end();
		it_t4 = o4.begin(); ite_t4 = o4.end();
		it_t5 = o5.begin(); ite_t5 = o5.end();
		it_t6 = o6.begin(); ite_t6 = o6.end();
	}

	// return:
	// true on success
	// false if limit reached
	bool increment()
	{
		do
		{
                        process(1);
                        process(2);
                        process(3);
                        process(4);
                        process(5);
                        process(6);
                        // Limit reached
			return false;

		} while ( 0 );
		return true;
	}
        def(1);
        def(2);
        def(3);
        def(4);
        def(5);
        def(6);
private:
	MultiTypeCounter(const MultiTypeCounter&);
	MultiTypeCounter& operator=(const MultiTypeCounter&);
}
monk ★★★★★
()

Ух ты, простыня.

Сразу возникает такой вопрос: а что, у вас прямо таки все шесть типов, вот-так вот и могут поменяться? Т.е. реализуется 100500 конкретизаций этого шаблона? Я бы, на вашем месте, запилил бы какой-то класс-обертку над этими типами, чтобы в приведенном шаблоне передавать один параметр - класс-мультитип. Далее, если вам производительность не так важна, то можно не хардкодить итераторы, а хранить их, например в векторе. Тогда можно будет перебирать их в цикле, а не копипастить if-ы.

no-such-file ★★★★★
()

Если размерность фиксирована, то лучше так оставить, наверно. Длинно, но очевидно и читаемо. Другие варианты, например с рекуррентными шаблонами, будут короче, но хуже.

dmfd
()
Ответ на: комментарий от dmfd

Он прав, а Вы нет.

По сабжу - либо рекуррентно делать, либо через С++11 - тоже будет рекурретно, но чуть симпатичнее в использовании.

AIv ★★★★★
()

почему не использовать 6 объектов класса counter и класс счетчика по вектору таких объектов?

dib2 ★★★★★
()
Ответ на: комментарий от dib2

Потому что вектор нельзя (без шаблонных извращений или полиморфизма) собрать из разнотипных объектов.

Хотя, если у ТС нету супер-пупер требований по производительности, то можно завести абстрактный базовый класс с виртуальным доступом к итерации, отнаследовать его шаблоном, и дальше ваять все на векторе - тоже будет короче и проще для неокрепшего ума;-)

AIv ★★★★★
()
Ответ на: комментарий от AIv

ну собственно с полиморфизмом, само-собой.

dib2 ★★★★★
()
Ответ на: комментарий от AIv

А ничего, что шаблоны и макросы на разных стадиях сборки раскручиваются, и в одном случае работа идёт с обычными строками, а в другом — с конструкциями, связанными с семантикой языка, c понятными последствиями? Говорить о равенстве шаблонов и макросов в C++ — настоящее невежество. Они работают совершенно по-разному, обладают разными возможностями и недостатками.

dmfd
()
Ответ на: комментарий от dmfd

Если смотреть узко, то они неравны. Если смотреть чуть пошире, обращая внимание на функциональность а не на детали реализации, и сравнивать с другими ЯП - то вполне сопоставимые вещи.

AIv ★★★★★
()
Ответ на: комментарий от AIv

и сравнивать с другими ЯП

Товарищ выше как раз и исходил из определённого другого ЯП. А что толку, если мы говорим о C++?

Если смотреть чуть пошире, обращая внимание на функциональность

...станет видно, что она разная, и эти вещи не взаимозаменяемы.

dmfd
()
Ответ на: комментарий от dmfd

Есть мнение, что даже в плюсах макросы позволяют делать то же самое,что и шаблоны (с точностью до синтаксиса и некоторых фигней типа рекурсии, хотя в бусте и это решили). Если хотите заниматься буквоедством - пожалуйста, но тогда сначала дайте определение макроса (не привязанное к С++ а мультиязыковое). Потому как хочу напомнить, что эта содержательная дискуссия началась вот с этого поста, т.е. ее зачинщик уже вышел за рамки С++. И сравнивались не плюсовые макросы, а лисповые макросы и плюсовые шаблоны. А я имел ввиду макросы вообще (я и в питоне вообще то макросы для себя ввел, хотя их там изначально не было). Макрос понятие куда более чем широкое, чем то что дает #define.

а что толку, если мы говорим о С++.

Это ВЫ говорите только о С++. А я привык смотреть чуток пошире;-)

AIv ★★★★★
()
Последнее исправление: AIv (всего исправлений: 1)

Сея логика не обсуждается, она нужна.

Думаю, что не нужна. Попробуйте прочитать про ranges. Например, можно объединить несколько диапазонов в один.

Если же не хотите возиться и хочется оставить как есть, то могу предложить посмотреть в сторону variadic-template(если есть новый стандарт) или «рекурсивного» наследования, если стандарт старый. Будет время, могу расписать подробнее.

С другой стороны, может и правда взять чистый С? Там таких вопросов бы не возникло, а код был бы уже написан...

forCe
()
Ответ на: комментарий от monk

Шаблоны - это не макросы. Т.е. вообще. Они не подставляют текст(как сишные макросы), они не работают с AST(как макросы в «нормальных» языках). Это совершенно отдельный механизм, но он тесно интегрирован с остальным языком и вы не можете даже выделить фазу инстанцирования в отдельную фазу по времени.

forCe
()
Ответ на: комментарий от monk

Я бы пожалуй уволил написавшего такое падавана. Ну или бы поговорил для начала.

forCe
()
Ответ на: комментарий от forCe

Давайте сначала вы объясните, на каком основании вы думаете, что такая логика не нужна? А потом, когда мы поймём, что она не нужна, я сам прекрасно найду решение )

kiverattes ★☆
() автор топика
Ответ на: комментарий от kiverattes

Щас объяснит. У товарищей с таким категоричным мЫшлением либастрал работает как надо. Готовьтесь, Вы еще будете ему долго объяснять что он не о том, и так не объясните;-)

AIv ★★★★★
()

А в чем сакральный смысел конструкции

do{
...
} while(0);

Что не позволяет натыкать ретурнов вместо бряков?

Можно ли оформить increment как функцию?

template <class T1, class T2, ...> bool increment(T1&, T2& ...){...}

AIv ★★★★★
()
Ответ на: комментарий от forCe

Они скорее похожи на обобщенные типы в Хаскеле, не так красиво записываются, но работают похожим образом. А всякими type_traits'ами можно эмулировать и классы типов.

Gorthauer ★★★★★
()
Ответ на: комментарий от Gorthauer

Спасибо К.О. Я с-но хотел спросить у ТС-а, устраивает ли его по дизайну такой вариант (который самый лаконичный) - если мутить через класс букв куда будет больше.

AIv ★★★★★
()
Ответ на: комментарий от AIv

Не, у него всего 1 попытка, я запариваться не буду.

kiverattes ★☆
() автор топика
Ответ на: комментарий от Gorthauer

Че то я злой какой то сегодня, невыспавшийся. Там ище должен был смайлик быть ";-)".

AIv ★★★★★
()
Ответ на: комментарий от AIv

Сакральный смысл просто в таком стиле написания кода, разницы нет.

Как функцию - неудобно, ведь надо в неё постоянно передавать эти итераторы, а значит состояние счётчика (суть сумма состояний итераторов) будет храниться за пределами этого моего класса, что идеологически неправильно, ведь счётчик, коим является этот класс, сам должен помнить своё считательное состояние, не возлагая труд памяти на своего пользователя.

kiverattes ★☆
() автор топика
Ответ на: комментарий от kiverattes

что идеологически неправильно

Откройте для себя ФП!

ведь счётчик, коим является этот класс, сам должен помнить своё считательное состояние,

Предлагается организовывать счетчик как угодно. Основная фича ж в инкременте, и вот его оформить как ф-ю. С++0x11 юзать можно?

AIv ★★★★★
()
Ответ на: комментарий от AIv

C++0x11 наверное можно, но я сам о нём практически не читал. Можно поподробнее о функциональном варианте реализации (можно за рамками С++ вообще :))

kiverattes ★☆
() автор топика
Ответ на: комментарий от kiverattes

Мдя... насчет ФП я загнал, потому что предлагаемый вариант будет менять аргументы. Но все равно это ближе к ФП, перегружать ф-ии в крестах проще чем классы специфицировать, да и иногда удобней разносить код и данные.

В С++0х11 есть такая шняга как Variadic template, самый простой пример с printf-ом http://alenacpp.blogspot.ru/2008/08/variadic-templates.html

AIv ★★★★★
()
Ответ на: комментарий от kiverattes

Если про чиста функциональный вариант, то есть вектор (массив) счетчиков, есть ф-я возвращающая массив в след. состоянии и ф-я проверяющая что наступил конец. Так и в плюсах можно сделать, но вообще говоря обычная дубовая императивщина в этом случае будет работать быстрее.

AIv ★★★★★
()

У тебя интуитивное желание засунуть это в цикл какой-нибудь, что бы не дублировать код. Но тебе для этого нужны списки элементов произвольного типа (или хотя бы на 6ть элементов). А в спп такого нету вроде. Сделай интерфейс какой-нибудь и храни список ссылок на интерфейсные классы. Тогда можно будет сделать for_each.

Да, и ещё. Каноничнее запоминать не ссылку на элемент, а только ссылку на его begin Т.е. вместо

const T1 &o1; (в приватных полях)
const T2 &o2;
я бы сделал
itb_t1 = o1.begin(); (в конструкторе)
...
typename T1::iterator itb_t1;(в приватных полях)

nanoolinux ★★★★
()
Ответ на: комментарий от nanoolinux

Что-то типа такого, рекурсивного бреда. Типы для итераторов не указаны, потому-что они разные, а спп нужен какой-то один. У тебя же их аж 6ть.


struct Value {
  iterator begin,
  iterator current
  const iterator end,
};

bool increment(list <Value>::iterator it, const list <Value>::iterator &end)
{
  if (it == end) {
    return false;
  } else {
    if (it->current != it->end) {
      ++(it->current);
    }
    if (it->current != it->end) {
      return true;
    } else {
      it->current = it->begin;
      return increment(++it, end);
    }
  }
}

nanoolinux ★★★★
()
Последнее исправление: nanoolinux (всего исправлений: 3)
Ответ на: комментарий от kiverattes

Давайте сначала вы объясните, на каком основании вы думаете, что такая логика не нужна?

На том, что это уродливо. Я просто предполагаю, что если подумать лучше над задачей, можно к этому уродству не приходить. Вашей задачи я не знаю. Чтобы сказать что-либо более конкретное, нужно знать больше.

forCe
()
Ответ на: комментарий от AIv

A macro (short for «macroinstruction», from Greek μακρο- 'large') in computer science is a rule or pattern that specifies how a certain input sequence (often a sequence of characters) should be mapped to a replacement input sequence (also often a sequence of characters) according to a defined procedure. The mapping process that instantiates (transforms) a macro use into a specific sequence is known as macro expansion.

Как Вы сами понимаете, шаблоны С++ под это определение не попадают. Они не являются правилами/паттернами для описания замены ни для текста, ни для AST.

forCe
()
Ответ на: комментарий от forCe

Чтобы сказать что-либо более конкретное, нужно знать больше.

Ну вы сказали более чем конкретно - что такая логика не нужна. Куда конкретнее?

kiverattes ★☆
() автор топика
Ответ на: комментарий от kiverattes

Вам задачу решать или спорить? Не хотите задумываться о том, как вы пришли к этому уродству и не стоит ли перепроектировать что-либо - ваше право.

Вернемся к нашим баранам. Попробуйте так(это С++11):

template<typename T, typename ... Args>
class MultiTypeCounter : MultiTypeCounter<Args...> {
public:
    typedef MultiTypeCounter<...Args> Parent;
    typedef typename T::const_iterator const_iterator;
    typedef typename T::const_iterator const_iterator;

    MultiTypeCounter(const T & o_, Args ... args)
        : Parent(args...),
          o(o_),
          it(o.begin()),
          it(o.end())
    {
    }

    bool increment()
    {
        if (it != ite) {
            ++it;
        }
        if (it != ite) {
            return true;
        }

        it = o.begin();

        return Parent::increment();
    }

private:
    const T &o;
    const_iterator it, ite;
};

template<typename T>
class MultiTypeCounter<T> {
public:
    typedef typename T::const_iterator const_iterator;
    typedef typename T::const_iterator const_iterator;

    explicit MultiTypeCounter(const T & o_)
        : o(o_),
          it(o.begin()),
          it(o.end())
    {
    }

    bool increment()
    {
        if (it != ite) {
            ++it;
        }
        if (it != ite) {
            return true;
        }

        it = o.begin();
        return false;
    }

private:
    const T &o;
    const_iterator it, ite;
};

Это набросок для иллюстрации идеи. Нужно причесать(хотя бы убрать повторяющийся код из специализации.

forCe
()
Ответ на: комментарий от forCe

Я тут мог немного напутать с порядком ... в вариадиках, код не проверял. Надеюсь, идея ясна.

forCe
()
Ответ на: комментарий от forCe

Как Вы сами понимаете, шаблоны С++ под это определение не попадают. Они не являются правилами/паттернами для описания замены ни для текста, ни для AST.

Отчасти таки являются. Например:

template <typename T> class vector{
   T* p;
...
   T& operator [](int i){ return p[i]; }
};
...
   vector<int> p1;
   vector<double> p2;
...

Т.е. шаблон - это как раз таки правило для генерации каких то законченных фрагментов кода (классов или функций). Детали реализации для конечного пользователя абсолютно неважны.

AIv ★★★★★
()
Ответ на: комментарий от AIv
vector<int> p1;
vector<double> p2;

Тут нет генерации кода. Тут используется два типа vector<int> и vector<double>. Более того, если это все, что вы написали, то в обоих типах не будет указанных вами operator[](он в них будет только тогда, когда вы воспользуетесь им).

Макрос можно вызывать. Было бы неплохо, если бы вы показали мне вызов шаблона.

шаблон - это как раз таки правило для генерации каких то законченных фрагментов кода

Нет, не законченных. И нет там никакой генерации. Есть инстанцирование. Если не верете - помедитируйте, например, на тему правил сочетания шаблонов функций, перегрузки функций и специализации шаблонов функций. Если не поможет, подумайте над SFINAE.

Шаблон - это скорее гетерогенный полиморфный тип(гомогенные - дженерики).

forCe
()
Ответ на: комментарий от forCe

Терминологические споры самые бессмысленные. С т.з. конечного пользователя явная разница между макросами (не крестовыми «а вообще») и шаблонами не прослеживается. Да, детали реализации различаются. Да, в крестовых шаблонах в отличии от макросов (в большинстве других ЯП) есть контроль типов и даже механизмы позволяющие переключаться между различными возможными ветками «генерации кода» (тот же SFINAE), и выбирать корректную ветку.

Поверьте, в том же питоне макросов нету но я могу их туда засунуть если возникнет острая необходимость, и даже аналог SFINAE и контроль типов прикрутить. Да, это будет ближе к этому жуткому названию «гетерогенный полиморфный тип(гомогенные - дженерики)», но в Лиспе что то мне подсказывает что макросы это тоже не просто макросы?;-)

ЗЫ и чем же генерация кода так прЫнЦыпиально отличается от инстацирования?

AIv ★★★★★
()
Последнее исправление: AIv (всего исправлений: 1)

С точки зрения совсем конечного юзера, который настолько конечен, что проще застрелиться, бытие вообще тщетно и в гробу он видал все эти ваши компьютеры, лишь бы было чего пожрать.

kiverattes ★☆
() автор топика

Можно как-то так:

class Holder {
    public:
    virtual bool increment() = 0;
};

template<class T>
class HolderImpl : public Holder {

    T iterator;
    T end;

    public:
    HolderImpl(T const& _begin, T const& _end) : iterator(_begin), end(_end)
    {}

    bool increment() override
    {
        if (iterator != end)
        {
            ++iterator;
            return true;
        }
        return false;
    }
};

template<class T, class U, class ... Args>
class Iter {
    public:
    Iter(T& vector, U const& u, Args const& ... args)
    {
        vector.push_back(new HolderImpl<typename U::const_iterator>(u.begin(), u.end()));
        Iter<T, Args...>(vector, args...);
    }
};

template<class T, class U>
class Iter<T, U>
{
    public:
    Iter(T& vector, U const& u)
    {
        vector.push_back(new HolderImpl<typename U::const_iterator>(u.begin(), u.end()));
    }
};



template<class ... Args>
class MultiTypeCounter {
    std::vector<Holder*> iterators;
    size_t currentIndex = 0;

    public:
    MultiTypeCounter(Args const&... args) {
        Iter<std::vector<Holder*>, Args...>(iterators, args...);        
    }

    bool increment()
    {
        while (currentIndex < iterators.size())
        {
            if (iterators[currentIndex]->increment()) {
                return true;
            }
            currentIndex++;
        }
        return false;
    }
};

BlackHawk
()
Ответ на: комментарий от kiverattes

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

AIv ★★★★★
()
Ответ на: комментарий от AIv

В вашем сраче с forCe я на твоей стороне, но в общем этот срачь к топику отношения не имеет, на вопросы от не отвечает )

kiverattes ★☆
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.