LINUX.ORG.RU

с++, сгенерировать инстансы шаблона?

 , ,


0

1

Например, есть шаблонный класс, например

template<int nd>
class foo_t{
	...
};
И холдер, который может держать несолько инстансов, например через
class foo_holder : public std::variant<foo_t<1>, foo_t<2>>{
	...
	template<int nd>
	operator foo_t<nd>& () const {
		return std::get<foo_t<nd>>(*this);
	}
};
Ну или через union.

И, например, есть шаблонная функция, принимающая этот шаблонный класс и в то же время параметризуемая ещё какими-то параметрами

template<int nd, int flag>
void proc_foo_f(foo_t<nd>&)
Хочется какого-то аналога виртуального метода так что бы была одна универсальная функция, принимающая холдер класса, параметры и вызывающая внутри себя уже конкретные инстансы шаблона. Т.е. вручную по смыслу это было-бы что-то вроде
void proc_foo(foo_holder& foo, int a, int b){
	int nd=foo.index()+1
	switch (nd) {
		case 1: switch (flag) {
			case FLAG1: return proc_foo_f<1,1>(foo); 
			case FLAG2: return proc_foo_f<1,2>(foo);
 			...
		}
		case 2: switch (flag) {
			case FLAG1: return proc_foo_f<2,1>(foo); 
			case FLAG2: return proc_foo_f<2,2>(foo); 
 			...
		}
	}
}
Где число комбинаций nd и флагов конечно, поэтому, в принципе, всё это можно занести в таблицу.

Думал сделать это ввиде виртуального класса-функтора, но как-то это тоже коряво, тем более для каждого инстанса его надо не только писать, но ещё и называть как-то. Как такую семантику делать правильно?

★★★★★

Последнее исправление: cetjs2 (всего исправлений: 4)
Ответ на: комментарий от anonymous

oops, не заметил что у proc_foo_f тоже возвращаемый тип void.

anonymous
()

А в чём, собственно, разница, если в примере в разных кейсах одни и те же параметры шаблона?

seiken ★★★★★
()

Больше шаблонов Богу шаблонов!;-)

А сделать абстрактный базовый класс и отнаследовать его шаблоном?

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

Мм, можно схемку как это работать должно? Мне не понятно, можно ли средствами языка сгенерировать инстансы, или всё таки нужна кодогенерация.

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

Ты, наверное, хочешь странного и можно как-то по-другому, но всё же родил вот такое:

#include <variant>
#include <array>
#include <iostream>
using namespace std;

template<int nd>
class foo_t{
	std::variant<int,double> d;
};

#define FOO_LIST    \
	LIST_HELPER(0), \
	LIST_HELPER(1), \
	LIST_HELPER(2), \
	LIST_HELPER(3), \
	LIST_HELPER(4)

#define LIST_HELPER(foo_ind) foo_t<foo_ind>
class foo_holder : public std::variant<FOO_LIST>{
#undef LIST_HELPER
	template<int nd>
	operator foo_t<nd>& () const {
		return std::get<foo_t<nd>>(*this);
	}
};

enum class Flags {one, two, three, size};

template<int nd, Flags flag>
void proc_foo_f(void *data) {foo_t<nd> *p = static_cast<foo_t<nd>*>(data);}

consteval auto prof_foo_f_array()
{
	static_assert(static_cast<int>(Flags::size) == 3);
#define LIST_HELPER(foo_ind)             \
	&proc_foo_f<foo_ind, Flags::one>,    \
	&proc_foo_f<foo_ind, Flags::two>,    \
	&proc_foo_f<foo_ind, Flags::three>
	array res {
		FOO_LIST
	};
#undef LIST_HELPER
	return res;
}

int main() {
	auto a = prof_foo_f_array();
	cout << a.size() << endl;     // 15
	for (auto f : a)
		f(nullptr);
	return 0;
}

ЗЫ: индексация в массиве - не проблема, естественно.
Хотя твою задачу целиком все же не решил, не продумал каст здесь: case FLAG1: return proc_foo_f<1,1>(foo);

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

так как число комбинаций флагов конечно, то можно запихнуть их в еще один variant, и вызывать нужный foo_t в std::visit. Но при этом конечно может быть оверхед.

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

Хотя твою задачу целиком все же не решил

Не, все же решил. Ты говорил, что можно юнион, его кастуем в войд, в обработчике кастуем обратно. Должно работать.

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

Задача целиком:

#include <variant>
#include <array>
#include <iostream>
using namespace std;

template<int nd>
struct foo_t{
	std::variant<int,double> d;
};

#define FOO_LIST    \
	LIST_HELPER(0), \
	LIST_HELPER(1), \
	LIST_HELPER(2), \
	LIST_HELPER(3), \
	LIST_HELPER(4)
#define LIST_HELPER(foo_ind) foo_t<foo_ind>
using variant_t = std::variant<FOO_LIST>;
#undef LIST_HELPER

class foo_holder : public variant_t {};

enum class Flags {one, two, three, size};

template<int nd, Flags flag>
void proc_foo_f(variant_t &data) {
	auto &v = std::get<foo_t<nd>>(data);
	cout << std::get<int>(v.d) << endl;
}

consteval auto prof_foo_f_array() {
	static_assert(static_cast<int>(Flags::size) == 3);
#define LIST_HELPER(foo_ind)             \
	&proc_foo_f<foo_ind, Flags::one>,    \
	&proc_foo_f<foo_ind, Flags::two>,    \
	&proc_foo_f<foo_ind, Flags::three>
	array res {
		FOO_LIST
	};
#undef LIST_HELPER
	return res;
}

void proc_foo(foo_holder& foo, int a, int b) {
	auto ar = prof_foo_f_array();
	ar[a*static_cast<int>(Flags::size)+b](foo);
}

int main() {
	foo_holder f1;
	proc_foo(f1, 0, 2);
	foo_holder f2{ variant_t{foo_t<2>{}} };
	proc_foo(f2, 2, 2);
	int in;
	cin >> in;
	proc_foo(f2, in, 0);

	return 0;
}
pavlick ★★
()

по-моему это делается через специализацию:

template <int n> do_nd();

потом специализируешь:

template <> do_nd<1>() { return proc_foo_f<1,1>(foo); }

ну как-то так, я не компилировал код. такую куйню надо с открытым справочником писать. но потом вызываешь:

do_nd<nd>();

а компилятор сам выберет наиболее подходящую специализацию.

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