Как предоставить доступ void * для шаблонного типа? - PullRequest
1 голос
/ 05 мая 2019

У меня есть пользовательский контейнерный класс с шаблоном:

template<typename T>
class MyContainer {
  T Get();
  void Put(T data);
};

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

// Errors of course, no template argument
void DoSomething(MyContainer *container);

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

Я рассмотрел возможность вообще избегать шаблонов, заставив MyContainer содержать контейнер некоторого абстрактного MyData класса, который имеет функцию virtual void Serialize(void *dest) = 0;. Пользователи могут создавать подклассы MyData для предоставления своих типов и сериализации, но кажется, что это становится довольно сложно. Также неэффективно, так как требует хранения указателей на MyData, чтобы избежать нарезки объектов, а MyData обычно довольно мал, и контейнер будет содержать большие объемы (много хранения указателей и разыменование).

Ответы [ 3 ]

1 голос
/ 05 мая 2019

Вам не нужно char* или void* или наследование.

Рассмотрите эту упрощенную реализацию:

template <class T>
void Serialize (std::ostream& os, const MyContainer<T>& ct) {
  os << ct.Get();
}

Внезапно это работает для любогоT с подходящей перегрузкой operator<<.

Как быть с типами пользователей, у которых нет подходящей перегрузки operator<<?Просто попросите пользователей предоставить один.

Конечно, вы можете использовать любую перегруженную функцию.Это не должно быть названо operator<<.Вам просто нужно сообщить его имя и подпись пользователям и попросить их перегрузить его.

0 голосов
/ 05 мая 2019

Я хотел бы передать указатель на этот контейнер в функцию, которая будет обращаться к данным контейнера как к универсальным данным - то есть char* или void*.Подумайте о сериализации.

Не может быть сделано вообще, потому что вы ничего не знаете о T.Как правило, типы нельзя обрабатывать (например, копировать, получать к ним доступ и т. Д.) В виде необработанных больших двоичных объектов с помощью char * или аналогичных.

Поэтому вам необходимо ограничить то, что может быть T, в идеальном случае принудительно применяя егоиначе никогда не используйте его для T s, который вызовет неопределенное поведение.Например, вы можете утверждать, что std::is_trivially_copyable_v<T> выполнено.Тем не менее, вам придется учитывать другие возможные проблемы при обработке таких данных, как порядковый номер и упаковка.

Эта функция несколько сложна, поэтому было бы неплохо не указывать ее в заголовке из-заШаблоны.

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

Я решил вообще избегать шаблонов, заставив MyContainer содержать контейнер некоторого абстрактного MyData класс с функцией virtual void Serialize(void *dest) = 0;.Пользователи будут создавать подклассы MyData для предоставления своих типов и сериализации, но кажется, что это становится довольно сложно.Также неэффективно, поскольку требует сохранения указателей на MyData, чтобы избежать нарезки объектов, а MyData обычно довольно мал, и контейнер будет содержать большие объемы (большое количество хранилищ указателей и разыменование).

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


В заключение я бы посоветовал взглянуть на некоторые доступные библиотеки сериализации, чтобы увидетькак они достигли этого, не только с точки зрения производительности, но и с точки зрения простоты использования, интеграции с существующим кодом и т. д. Например, Boost Serialization и Буферы протокола Google .

0 голосов
/ 05 мая 2019

Вы можете ввести не шаблонный базовый класс для контейнера с чисто виртуальной функцией, которая возвращает указатель на необработанные данные и реализует его в вашем контейнере:

class IDataHolder
{
public:
    virtual ~IDataHolder(); // or you can make destructor protected to forbid deleteing by pointer to base class
    virtual const unsigned char* GetData() const = 0;
};

template<typename T>
class MyContainer : public IDataHolder
{
public:
  T Get();
  void Put(T data);
  const unsigned char* GetData() const override { /* cast here internal data to pointer to byte */}
};

void Serialize(IDataHolder& container)
{
    const auto* data = container.GetData();
    // do the serialization
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...