Я собираюсь изменить формулировку своего вопроса, поскольку люди, похоже, не понимают, какова цель.
Поскольку я получаю массу комментариев, предлагающих попробовать то, что я уже пробовал, я думаю, что лучше сделать это с самого начала. Прошу прощения, если сообщение получилось длинным.
Давайте попробуем так:
Чего мы пытаемся достичь< /p>
- Я хочу написать библиотеку, которая вызывает метод известной формы для пользовательских типов.
- Я хочу этот метод принимает в качестве параметров другие типы, определяемые пользователем.
- Обе категории этих типов могут иметь любое количество дополнительных методов и пользовательских элементов данных, которые может придумать пользователь.
- На момент написания библиотеки я не знал подробностей об этих типах.
- Определения типов мы узнаем во время компиляции.
- Все эти типы должны содержаться в стандартном типе коллекции, таком как вектор или очередь.
В 1990 году я бы делал это с базовыми классами, из которых пользователь извлекает состояния и события, и просил бы их поместить свои пользовательские данные в конкретные классы, дайте мне какой-то механизм определения типов, зарегистрировать их типы в моей библиотеке, используя эти идентификаторы типов, а затем выполнить для них динамическое приведение, когда придет время выполнять обратные вызовы, но я хочу этого избежать.
Мой библиотека предоставит
Код: Выделить всё
class IEvent
{};
class IState
{
public:
virtual void enter(std::shared_ptr event) = 0;
virtual void exit(std::shared_ptr event) = 0;
}
Код: Выделить всё
struct PedCrossingEvent : IEvent
{
int m_customUserData;
};
struct TimerElapsedEvent : IEvent
{
std::string m_othercustomUserData;
};
class GreenState : IState
{
public:
virtual void enter(std::shared_ptr event)
{
// We'd need to perform the dreaded dynamic_cast to get to the concrete type in order to access the user's custom data.
}
virtual void exit(std::shared_ptr event)
{
// We'd need to perform the dreaded dynamic_cast to get to the concrete type in order to access the user's custom data.
}
};
Конечно, я слышал: «Если вы используете Dynamic_cast, дизайн неправильный». Большой! Как нам сделать это прямо тогда? Вы не можете использовать виртуальные методы, чтобы решить проблему незнания того, какие данные пользователь хочет включить в их различные конкретные типы. Если только вы не захотите использовать std::any или void *. Это их путь? Не знаю.
Посетитель – попытка 2
Итак, я немного погуглил и наткнулся на переговоры CppCon по стиранию типов. и шаблон посетителя. «Эй, может быть, это решит мои проблемы!»
Ну, стирание типов позволяет мне помещать любой тип в один и тот же контейнер, поэтому это отвечает одному требованию, но мне все равно нужно знать конкретные типы, чтобы получить определенные пользователем данные, и приведение должно произойти где-то во время или перед вызовом State::enter и State::exit.
Введите шаблон посетителя.< /p>
Рассмотрите следующий листинг кода и представьте, что я пишу библиотеку, которая содержит все, начиная со строки using Event. А типы над этой строкой предоставлены пользователем моей библиотеки.
Код: Выделить всё
mainМне нужно сделать вызов чтобы указать::enter и вернуть пользователю тип настраиваемого события в качестве параметра.
Мне нужно сохранить типы настраиваемых событий пользователя в контейнере.
Мне нужно хранить пользовательские типы состояний в контейнере.
Эти типы и их детали неизвестны на момент написания библиотеки. Они универсальны. Однако они будут известны во время компиляции.
Код: Выделить всё
#include
#include
#include
// User defined types examples
struct PedCrossingEvent{int m_myCustomUseData;};
struct ProgramTimeElapsedEvent{double m_myOtherUserCustomData;};
struct NoProgramEvent{std::string m_otherDataStill;};
class Elephant;
struct GreenState
{
Elephant * m_customStateData;
void enter(std::shared_ptr
predCrossingEvent)
{
if (predCrossingEvent.m_myCustomUseData > 10)
{//...}
}
void enter(std::shared_ptr programTimeElapsedEvent)
{
if( programTimeElapsedEvent.m_myOtherUserCustomData < 100.0 )
{//...}
}
void enter(std::shared_ptr noProgramEvent)
{
if( noProgramEvent.m_otherDataStill< 100.0 )
{//...}
}
void exit(std::shared_ptr pedCrossingEvent)
{}
void exit(std::shared_ptr programTimeElapsedEvent)
{}
void exit(std::shared_ptr noProgramEvent)
{}
};
// How do I, at compile time, register user defined types from above, that could be anything the user dreams up, with this variant in my library?
// I need some kind of "register type" compile time mechanism
using Event = std::variant;
int main()
{
std::vector events = {
std::make_shared(),
std::make_shared(),
std::make_shared()
};
GreenState green;
std::visit([&green](auto&& arg) { green.enter(arg); }, events[0]);
return 0;
}
Проблема здесь в том, что оператор using должен существовать в моей библиотеке, и я не знаю, какие типы будет использовать пользователь. сделать в будущем. Даже если это будущее действительно будет временем компиляции, а не временем выполнения.
Мне интересно, существует ли какой-то механизм (возможно, магия шаблонов), позволяющий мне взять некоторый список типов, предоставленный пользователем, и зарегистрируйте их, создав std::вариант с использованием этих типов.
В настоящее время я формирую тему вопроса по этому вопросу.
Вниз кроличья нора в забвение
Я могу попытаться обернуть оператор using в шаблонный тип, но мне кажется, что я только перемещаю проблему в другое место. Пользователь все равно должен как-то сообщать мне о типах. Как я могу создать для них метод регистрации, не зная параметров типа?
Код: Выделить всё
/*
* I needed a way to define the variant at compile time without knowing the user defined types
*/
template
class RegisteredEventTypes
{
// Helper to determine if a type is part of the variant
template
struct isVariantMember;
// Helper to determine if a type is part of the variant
template
struct isVariantMember : public std::disjunction {};
public:
using EventVariantType = std::variant;
static constexpr size_t getNumEventTypes()
{
return std::variant_size_v;
}
template
static constexpr bool isTypeRegistered()
{
return isVariantMember ::value;
}
};
int main()
{
// Try out the template. The user will need to supply this to a registration method, but how can I make such a method without knowing the type params?
using MyEventTypes = RegisteredEventTypes;
// Test that it works
std::cout
Подробнее здесь: [url]https://stackoverflow.com/questions/78509561/register-types-for-stdvariant-programatically[/url]
Мобильная версия