@johnslowpoke

Одна сигнатура и несколько возвращаемых типов: как победить «двусмысленность»?

Есть сферический код в вакууме:

enum class Event {
	CREATE, TERMINATE, TIMEOUT, PARTYTIME
};

static std::map<Event, std::function<void()>> callbackMap {
	{ Event::CREATE, []{ std::cout << "CREATE\n"; } },
	{ Event::TERMINATE, []{ std::cout << "TERMINATE\n"; } },
	{ Event::TIMEOUT, []{ std::cout << "TIMEOUT\n"; } },
	{ Event::PARTYTIME, []{ std::cout << "PARTYTIME\n"; } },
};

struct App {
	
	struct Callback {
		using Create = int(*)(void *);
		using Terminate = void(*)(void *);
		using Timeout = void(*)(void *); // идентичен с Terminate
		using Partytime = const char *(*)(int, void *);
	};

	template <Event TEvent>
	static Callback::Create getCallback() {
		return [](void *) { callbackMap[TEvent](); return 42; };
	}

};

// костыльная функция, роль которой в рельном коде выполняет ОС
template <typename T>
void caller(T callback) {
	callback(nullptr);
}

int main() {

	auto create = App::getCallback<Event::CREATE>();

	// ОС дергает колбэк
	caller(create);

}


Как реализовать getCallback для остальных ивентов? Возможно, требуется немного шаблонной магии?
  • Вопрос задан
  • 260 просмотров
Пригласить эксперта
Ответы на вопрос 1
@johnslowpoke Автор вопроса
Получилось вот это...

enum class Event {
	CREATE, TERMINATE, TIMEOUT, PARTYTIME
};

static std::map<Event, std::function<void()>> callbackMap {
	{ Event::CREATE, []{ std::cout << "CREATE\n"; } },
	{ Event::TERMINATE, []{ std::cout << "TERMINATE\n"; } },
	{ Event::TIMEOUT, []{ std::cout << "TIMEOUT\n"; } },
	{ Event::PARTYTIME, []{ std::cout << "PARTYTIME\n"; } },
};

struct App {
	
	struct Callback {
		using Create = int(*)(void *);
		using Terminate = void(*)(void *);
		using Timeout = void(*)(void *); // идентичен с Terminate
		using Partytime = const char *(*)(int, void *);
	};

	template <typename TCallback, Event TEvent>
	static TCallback getCallback();

};

template <>
App::Callback::Create App::getCallback<App::Callback::Create, Event::CREATE>() {
	return [](void *) { callbackMap[Event::CREATE](); return 42; };
}

template <>
App::Callback::Terminate App::getCallback <App::Callback::Terminate, Event::TERMINATE>() {
	return [](void *) { callbackMap[Event::TERMINATE](); };
}

template <>
App::Callback::Timeout App::getCallback <App::Callback::Timeout, Event::TIMEOUT>() {
	return [](void *) { callbackMap[Event::TIMEOUT](); };
}

template <>
App::Callback::Partytime App::getCallback <App::Callback::Partytime, Event::PARTYTIME>() {
	return [](int, void *) -> const char * { callbackMap[Event::PARTYTIME](); return nullptr; };
}

// костыльная функция, роль которой в реальном коде выполняет ОС
template <typename T>
void caller(T callback) {
	callback(nullptr);
}

template <>
void caller(App::Callback::Partytime callback) {
	callback(0, nullptr);
}

int main() {

	auto create = App::getCallback<App::Callback::Create, Event::CREATE>();
	auto terminate = App::getCallback<App::Callback::Terminate, Event::TERMINATE>();
	auto timeout = App::getCallback<App::Callback::Timeout, Event::TIMEOUT>();
	auto partytime = App::getCallback<App::Callback::Partytime, Event::PARTYTIME>();

	// ОС дергает колбэки
	caller(create);
	caller(terminate);
	caller(timeout);
	caller(partytime);

}


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

UPD: менее нагруженный вариант, но придется держать в уме особые правила вызова для terminate и timeout

...
	template <typename TCallback>
	static TCallback getCallback();

	template <typename TCallback, Event TEvent>
	static TCallback getCallback();
...

	auto create = App::getCallback<App::Callback::Create>();
	auto terminate = App::getCallback<App::Callback::Terminate, Event::TERMINATE>();
	auto timeout = App::getCallback<App::Callback::Timeout, Event::TIMEOUT>();
	auto partytime = App::getCallback<App::Callback::Partytime>();


Даже не знаю, что хуже :/
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы