yaml-cpp, YAML::Node и оператор шаблона >>

#c #templates #yaml-cpp

#c #шаблоны #yaml-cpp

Вопрос:

Я делаю какую-то сериализацию с использованием yaml-cpp. Чтобы это сработало, каждый класс должен объявить метод с использованием подписи:

 template <typename T> void Serialize(Tamp; s);
 

Это T другой класс при сохранении и загрузке. Интерфейс двух классов одинаков, но я не могу создать абстрактный базовый класс, поскольку большинство методов являются шаблонами. Эта часть работает правильно. Я пытался подключить его с YAML::Node помощью ‘s operator>> и YAML::Emitter ‘s operator<< .

У operator<< меня есть рабочее решение, хотя и очень жестокое. Сначала объявите суперкласс для всех сериализуемых классов:

 template <typename T> class Serializable {};
 

Тогда я могу использовать следующее operator<< :

 template <typename T>
YAML::Emitteramp; operator<<(YAML::Emitteramp; out,
                          Serializable<T>amp; val)
{
    Serializer serializer(out);
    reinterpret_cast<T*>(amp;val)->Serialize(serializer);
    return out;
}
 

Это работает до сих пор, хотя это reinterpret_cast выглядит довольно страшно, и я не уверен, что это вообще законно. Я пробовал то же самое для operator>> , но это не сработало. Это выглядит так:

 template <typename T>
void operator>>(const YAML::Nodeamp; node,
                Serializable<T>amp; val)
{
    Deserializer deserializer(node);
    reinterpret_cast<T*>(amp;val)->Serialize(deserializer);
}
 

Но gcc (4.6.2) и clang (2.9) игнорируют его и используют operator>> определенный в nodeimp.h (часть yaml-cpp):

 template <typename T>
inline void operator >> (const Nodeamp; node, Tamp; value) {
    if(!ConvertScalar(node, value))
        throw InvalidScalar(node.m_mark);
}
 

Итак, мой вопрос: как мне это решить? Я абсолютно хочу иметь только один метод как для сериализации, так и для десериализации, и иметь возможность использовать >> и << , как если бы это был обычный тип, поддерживаемый yaml-cpp.

Ответ №1:

Во-первых, о reinterpret_cast : вы действительно хотите static_cast . В вашем случае вы знаете, что val это a T (а не просто a Serializable<T> ), поэтому вы можете использовать его напрямую.

Здесь я предполагаю, что вы объявляете свои классы как

 class Foo: public Serializable<Foo> { ... };
 

reinterpret_cast будет интерпретировать байты val как a T , что не гарантирует работу, но, вероятно, работает в вашем случае, потому что у вас одно наследование и Serializable<T> не добавляет никаких переменных-членов.

Далее, перейдем к вашей реальной проблеме: это ошибка в yaml-cpp, и теперь она исправлена (r52790a15757d, если вы следите за репозиторием mercurial, и смотрите http://code.google.com/p/yaml-cpp/issues/detail?id=126 для вопроса, который я открыл).

С приведенным выше исправлением я считаю, что ваш код должен работать. Если вы не следите за репозиторием, разница довольно мала — вы можете исправить ее в своей версии yaml-cpp.

Комментарии:

1. странно, я пробовал static_cast , и это не сработало, но теперь это работает. (Может быть, я пытался привести к T, а не к T *?). В любом случае ваш патч отлично работает, спасибо за исправление.