Черта, которая проверяет, имеет ли класс typedef (частный или другой) или нет

Есть ли способ проверить, есть ли у class typedef, который работает даже для private typedef?

Следующий код работает в VS2013, но не работает на gcc ideone

template<typename T>
struct to_void
{
    typedef void type;
};

class Foo
{
    typedef int TD;
};

template <typename T, typename dummy = void>
struct has_TD : std::false_type {};

template <typename T>
struct has_TD<T, typename to_void<typename T::TD>::type > : std::true_type{};

int main()
{
    std::cout << std::boolalpha << has_TD<Foo>::value << std::endl;
}

изменить — почему я хочу это

У меня есть собственная система сериализации, которая может сериализовать произвольный тип. У него есть несколько перегрузок, когда он должен вести себя по-разному (например, строка). Для остальных типов он просто записывает значение в память. Если у меня есть составной тип, я иногда могу просто записать в память (сохранение и загрузка происходят на той же архитектуре, скомпилированы одним и тем же компилятором, поэтому отступы будут одинаковыми и т. д.). Этот метод действителен, например, для типов POD (черта std::is_pod), но все типы POD являются лишь подмножеством всех типов, поддерживающих эту сериализацию.

Итак, у меня в основном есть шаблонная функция write<T>, которая просто записывает sizeof(T) байт (необработанная сериализация)... Но я не хочу, чтобы это вызывалось по ошибке, я хочу, чтобы пользователь явно сказал в своем классе: "этот класс/структура может быть сериализованным"). Я делаю это с помощью макроса ALLOW_RAW_SERIALIZE, который определяет некоторый typedef, который можно проверить с помощью типажа. Если класс MyClass не содержит typedef, вызов write(myClassInstance) вызовет ошибку компилятора.

Вещи, которые в основном решают, может ли класс быть сериализованным, - это его члены (без отражения члены не могут быть перечислены и проверены автоматически, поэтому пользователь должен предоставить такую ​​информацию). типичный класс выглядит так:

class
  public
    ctor-dtor
    methods
  private
    methods
    members

и я хочу, чтобы пользователи разрешали писать ALLOW_RAW_SERIALIZE как можно ближе к участникам, поэтому, когда они меняют некоторых членов, меньше шансов забыть об обновлении ALLOW_RAW_SERIALIZE (удалите его, когда он больше недействителен)

Вот почему я хочу проверить private typedef

Поскольку он заменяет отражение и берет весь тип и записывает его, я не врубаюсь в это, например, в нарушение инкапсуляции или что-то в этом роде...


person relaxxx    schedule 04.01.2016    source источник
comment
Это не кажется возможным. Поведение VS выглядит как ошибка.   -  person n. 1.8e9-where's-my-share m.    schedule 04.01.2016
comment
Просто любопытно: какой смысл знать, что есть typedef, который вы не можете использовать? Если бы это было возможно, это нарушило бы инкапсуляцию, поскольку изменения в деталях частной реализации влияют на клиентский код, несмотря на стабильность общедоступного API.   -  person Tony Delroy    schedule 04.01.2016
comment
@TonyD, пожалуйста, посмотри на мою правку   -  person relaxxx    schedule 05.01.2016
comment
Если ALLOW_RAW_SERIALIZE уже является макросом, то, конечно, вы можете расширить этот макрос, чтобы просто сделать вашу проверку сериализации friend?   -  person    schedule 05.01.2016
comment
Я пытался это сделать, но безуспешно: ideone.com/c5OyQu и ideone.com/fuqwWs возможно, я просто ошибся в синтаксисе, но если это работает, это было бы идеальным решением   -  person relaxxx    schedule 05.01.2016


Ответы (2)


ОБНОВЛЕНИЕ:

Ладно, провел небольшое исследование.

К вашему сведению, [вероятная] причина того, что ideone не скомпилировалась, заключается в том, что то, что вы делаете, требует -std=c++11 [или выше]. Я получил аналогичные ошибки, прежде чем добавить это. Но мне пришлось использовать clang++, так как g++ все еще были проблемы с компиляцией, если TD было private.

Но я не уверен, что это работает, поскольку единственная комбинация, которая печаталась верно, была, если TD был общедоступным. Все остальные общедоступные/частные и изменение TD на TF дали false. Возможно, VS2013 работает [почему?], но два других компилятора имеют проблемы либо в результатах компиляции, либо во время выполнения — YMMV.

Основой для того, что вы делаете, является std::integral_constant [начиная с С++ 11]. Похоже, что для того, что вы делаете, нет стандартного вывода из этого. То есть из http://www.cplusplus.com/reference/type_traits/integral_constant/ в списке признаков типа [слева] нет ничего, что соответствовало бы вашему варианту использования [AFAICT].

И у Boost.TypeTraits нет ничего, что совпадало бы [опять же, AFAICT].

Из книги Андрея Александреску: «Современный дизайн C++: применение общих шаблонов программирования и проектирования», раздел 2.10 Признаки типов:

Обычно вы будете писать свои собственные шаблоны трейтов и классы по мере необходимости в них для вашего общего кода. Однако некоторые черты применимы к любому типу. Они могут помочь универсальным программистам лучше адаптировать код шаблона к возможностям типа.

Так что "нормально" свернуть свой собственный, если хотите.

Но даже у TypeTraits, о котором он говорит [от Локи], опять же, нет ничего, что соответствовало бы тому, что ты делаешь.

Так как ни std, ни Boost ничего нет, то вопрос становится "что стандартно?" [с вашей точки зрения]. Может быть, где-то есть библиотека признаков С++ "fludger", которая имеет реализацию, но будет ли она считаться "стандартной"? YMMV

Тем не менее, вопрос или два:

Зачем это делать? Какая от этого польза? Как насчет защищенного typedef в базовом классе?

И это, похоже, требует знания частной части класса, и не будет ли это нарушением либо «скрытия данных», либо инкапсуляции [без объявления friend какого-либо рода]?

Итак, если последний вопрос верен, то вероятный [ИМО] ответ заключается в том, что нет стандартного способа сделать это, потому что это не то, что следует делать в стандартная библиотека.


Примечание: за эту часть проголосовали против (до того, как я [по-настоящему] понял вопрос). Я считаю, что оправдал себя выше. Так что не обращайте внимания на ответ ниже.

При использовании class видимость по умолчанию равна private. С struct это public.

Итак, либо выполните:

struct Foo

Or:

class Foo
{
    public:
    typedef int TD;
};

Это, конечно, при условии, что вы хотите TD быть public

person Craig Estey    schedule 04.01.2016
comment
Я не хочу указывать TD как public. Я спрашиваю, существует ли стандартный способ проверить наличие private typedef - person relaxxx; 04.01.2016
comment
Это не ответ на вопрос. - person juanchopanza; 04.01.2016
comment
@juanchopanza Понял, мой плохой. Но я ищу ответ. - person Craig Estey; 04.01.2016
comment
когда я говорю стандарт, я не думаю о существующих трейтах, которые выполняют эту работу, но я имею в виду специально написанные трейты, которые ведут себя в соответствии со стандартом С++ (не используя ошибки компилятора или что-то в этом роде). Пример ideone скомпилирован на C++14. Пожалуйста, смотрите редактирование моего вопроса, почему я хочу этого - person relaxxx; 05.01.2016
comment
Без проблем. Я поэтому и процитировал Андрея [про катание своих]. Но также он не будет отображаться в стандартной библиотеке. Таким образом, вы можете свободно реализовывать, не беспокоясь о [не] использовании стандартного метода (например, std::is_private). Но здесь g++ жалуется даже на -std=c++17 [clang++ доволен c++11]. Но программа не работает [я сделал 4 версии]. Ваш источник возвращает false. У меня есть идея, как заставить его работать так, как вы хотите. Если сделаю, отпишусь. Хорошо, почему [из вашего редактирования]. Возможно, есть еще более простой способ сделать это. - person Craig Estey; 05.01.2016

Если все, что вам нужно, это проверка времени компиляции, тогда должен выполняться следующий код:

#include <iostream>

class Foo
{
    typedef int TD;
    template<typename T> friend class has_TD;
};

template <typename T>
struct has_TD
{
    typedef typename T::TD type;
};

template <typename T, typename has_TD<T>::type = 0>
void write(const T& /*data*/)
{
    std::cout << "serialize" << std::endl;
}

int main()
{
    Foo foo;
    write(foo);
}
person boorg    schedule 05.01.2016