Pull to refresh

Comments 33

Я наверное чего-то не понял. Где рефлексия-то? Имена полей как из структуры достать и их-же тип в виде строки к примеру получить? В упор рефлексии не вижу… Пойду очки куплю, для чтения…
А рефлексия это не обязательно строки. Это в первую очередь возможность, не глядя на типы, вытаскивать из объектов свойства. Да, в данном случае вытаскивать можно только по индексу. Но это уже даёт как минимум возможность итерироваться по полям произвольных структур, что уже достижение для C++.

Тема очень крутая, за текст +100500! Не так часто происходят такие открытия.
Придирка к языку статьи
в русском языке используется «строка» кода, а не линия.
Ну в таком случае в С++ уже давно рефлексия есть, кто запрещает сделать так:
class A
{
private:
    int a, b;
    ...
}

void main()
{
    A* a = new A();
    int a_b = *(((int*)(a))+1);
    ...
}
В представленном вами случае, если вы измените структуру, например поле b сделаете std::string, у вас возникнет ошибка в рантайме, а не в момент компиляции.
Это была шутка, но я забыл, что не все на хабре понимают юмор ^_^
Какие открытия? Что вы за ерунду говорите? Хотя-бы в код nlohmann json, загляните, там этих открытий…
Заглянул, не нашёл ничего, напоминающего рефлексию или автоматизацию. Как на основе обсуждаемой статьи сделать автоматическую сериализацию/десерализацию json я догадываюсь (хотя имена всё равно не вытащить, придётся делать ключами индексы и битовые представления типов). А в предложенном вами nlohmann json предлагается руками написать to_json и from_json.
А в предложенном вами nlohmann json предлагается руками написать to_json и from_json.

С этой точки зрения json_dto от Sobjectizer team поудобней.

Прямо в README.md пример кода на C++:

json j2 = {
  {"pi", 3.141},
  {"happy", true},
  {"name", "Niels"},
  {"nothing", nullptr},
  {"answer", {
    {"everything", 42}
  }},
  {"list", {1, 0, 2}},
  {"object", {
    {"currency", "USD"},
    {"value", 42.99}
  }}
};


Вобщем все те-же самые механизмы, что и в статье используются, только не так топорно и без криков об открытиях, и механизмов побольше чутка.
Это построение DOM с использованием json value. Какое отношение имеет к рефлексии непонятно.
Вам не понятно, потому-что вы тупой. Это инициализация в C++14 с использованием nlohmann::json. Откуда вы с bfDeveloper взялись вобще? Из прошлого века выпали? Хабр как всегда пестрит идиотами.

У вас точно также нет рефлексии. Всё что вы делаете оно в compile time происходит. Ну о каких открытиях речь? Вот не вижу я их в упор. Перечислите, пожалуйста, что из механизмов C++11/14, используемых в статье, вы считаете открытием? Ещё можете сюда-же добавить typeid и type_info. И вообще будет мега-открытие.
Открытием я считаю возможность вот этого:
flat_get<FieldIndex>(value);

Да, это не runtime, это compile time reflection. От передаваемого value не требуется иметь специальные методы или перегружать специальные функции, или быть обработанным препроцессором. В то время как nlohmann json требует специальных методов.
Приведённый вами пример с json j2 = {...} создаёт объект типа json, а не типизированную структуру. Настоящая сериализация на основе рефлексии это что-то подобное:
MyCustomStruct obj = from_json<MyCustomStruct>("{member1:12, member2:[0.0, 3.14]}")

Такое без препроцессора на C++ не сделать, но в статье показан приём, делающий нас на шаг ближе к желаемому. Имея итерацию по полям мы можем, потребовав каким-либо образом указать имена полей, решить задачу полностью.
С какой радости «без препроцессора не сделать». И вообще, нет никакого открытия, — обращение к tuple с get делает ровно то-же самое, только не в рамках одной структуры. Внимание вопрос: В чём плюс обращения по номеру поля в структуре? Рефлексия требует человеко читаемых имён. Посмотрите в boost::multiindex, вот там рефлексия, там тегирование и имена индексов в compile time. А этот домострой вообще ни о чём.
Так в том и дело, что это не tuple, а любая структура. Преобразовать структуру в tuple не так-то просто без «этого домостроя». Можете предложить реализацию шаблона с таким объявлением?
template <typename T>
auto as_tuple(T&& t) -> tuple<Types...>;

Здесь Types… — список типов полей структуры T. Для любого конкретного типа всё тривиально, а обобщённо? В статье описано, как это сделать, а где ещё есть что-то подобное? Это и называется открытием — что-то, что до недавнего времени казалось невозможным, ну или никто подобного не делал.
boost::multiindex вообще мимо. Там тонны метапрограммирования, но к сабжу отношения не имеет.
UFO just landed and posted this here
И действительно — рефлексия это о runtime, а не compile time
А если в структуре будет union?
typedef struct _IMAGE_SECTION_HEADER {
  BYTE  Name[IMAGE_SIZEOF_SHORT_NAME];
  union {
    DWORD PhysicalAddress;
    DWORD VirtualSize;
  } Misc;
  DWORD VirtualAddress;
  DWORD SizeOfRawData;
  DWORD PointerToRawData;
  DWORD PointerToRelocations;
  DWORD PointerToLinenumbers;
  WORD  NumberOfRelocations;
  WORD  NumberOfLinenumbers;
  DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
Ещё не понял, что будет с массивами и указателями на массивы.
Не нравится мне этот подход с индексированием базовых типов и побочным эффектом у оператора приведения типа. Можно сделать перечисление базовых типов в виде SFINAE специализации шаблона. Например:
Код
#include <iostream>
#include <tuple>
#include <typeinfo>
#include <utility>

namespace reflect {

namespace detail {

template <size_t>
struct ubiq {
    template <class T>
    constexpr operator T&() const;
};

template <class T>
struct exact {
    constexpr operator T&() const;
};

template <class>
using void_t = void;

template <class T, size_t N = sizeof( T ), class S = std::make_index_sequence<N>, class = void>
struct tuple_size_helper : tuple_size_helper<T, N - 1, std::make_index_sequence<N - 1>> {
};

template <class T, size_t N, size_t... I>
struct tuple_size_helper<T, N, std::index_sequence<I...>, void_t<decltype( T{ubiq<I>{}...} )>> {
    using type = std::integral_constant<size_t, N>;
};

} // namespace detail

template <class T>
using tuple_size = typename detail::tuple_size_helper<T>::type;

namespace detail {

template <size_t N, class T, class = std::make_index_sequence<N>, class = std::make_index_sequence<tuple_size<T>::value - N - 1>,
          class = void>
struct tuple_element_helper;

template <size_t N, class T, size_t... I, size_t... J>
struct tuple_element_helper<N, T, std::index_sequence<I...>, std::index_sequence<J...>,
                            void_t<decltype( T{ubiq<I>{}..., exact<int>{}, ubiq<J>{}...} )>> {
    using type = int;
};

template <size_t N, class T, size_t... I, size_t... J>
struct tuple_element_helper<N, T, std::index_sequence<I...>, std::index_sequence<J...>,
                            void_t<decltype( T{ubiq<I>{}..., exact<size_t>{}, ubiq<J>{}...} )>> {
    using type = size_t;
};

template <size_t N, class T, size_t... I, size_t... J>
struct tuple_element_helper<N, T, std::index_sequence<I...>, std::index_sequence<J...>,
                            void_t<decltype( T{ubiq<I>{}..., exact<std::string>{}, ubiq<J>{}...} )>> {
    using type = std::string;
};

} // namespace detail

template <size_t N, class T>
struct tuple_element {
    using type = typename detail::tuple_element_helper<N, T>::type;
};

template <size_t N, class T>
using tuple_element_t = typename tuple_element<N, T>::type;

namespace detail {
template <class T, class = std::make_index_sequence<tuple_size<T>::value>>
struct as_tuple_helper;

template <class T, size_t... I>
struct as_tuple_helper<T, std::index_sequence<I...>> {
    using type = std::tuple<tuple_element_t<I, T>...>;
};
} // namespace detail

template <class T>
using as_tuple_t = typename detail::as_tuple_helper<T>::type;

} // namespace reflect

struct s {
    int a;
    size_t b;
    int x;
    std::string q;
};

int main()
{
    std::cout << reflect::tuple_size<s>() << std::endl;
    std::cout << typeid( reflect::tuple_element_t<0, s> ).name() << std::endl;
    std::cout << typeid( reflect::tuple_element_t<1, s> ).name() << std::endl;
    std::cout << typeid( reflect::tuple_element_t<2, s> ).name() << std::endl;
    std::cout << typeid( reflect::tuple_element_t<3, s> ).name() << std::endl;
    std::cout << typeid( reflect::as_tuple_t<s> ).name() << std::endl;
}

Сейчас на github лежит версия, которая может работать в C++14 с любыми пользовательскими типами данных и не использует их явное перечисление.

Она является чем-то отдалённо похожим на смесь вашего предложения и того что описано в статье: github.com/apolukhin/magic_get/blob/develop/include/boost/pfr/detail/core14_loophole.hpp

Буду рад любым предложениям по улучшению.
UFO just landed and posted this here

Очень интересно, но для реальной практики (вроде той задачи про 1000 структур и 3000 сериализаторов) лично я скорее взял бы внешний кодогенератор, чем пошёл по такому пути.

Снимаю шляпу. Автор этой магии продолжает дело Александреску.


Но согласен с предыдущим комментарием про кодогенерацию сторонними средствами. Боюсь даже представить как тут будет выглядеть ошибка компиляции и чего...

Внешняя кодогенерация требует проверки на то, что внешний кодогенератор отработал на стадии компиляции. И тут «эта магия» вот прямо вот сейчас мне помогает. Я могу хоть как-то проверить, что сборка проходит правильно.
Пора уже делать отладчик для шаблонов и всякой не очевидности сразу со ссылками на оправдания из стандарта.
Боже мой, неужели это читаемо?
Зачем делать на языке, который чисто академический, то для чего он не предназначен?
В общем-то подобный пост лишь еще одно свидетельство что на С++ писать не надо.
Вы специально открываете статьи по C++, чтобы в очередной раз убедиться в его сложности и сообщить об этом всем окружающим?
а вы бы хотели встретить такое в продакшн коде?..
Это не production код. Это код библиотеки.

В production будет 2 строчки и Вы автоматически получите (например) возможность сериализации любого POD типа.

Беспокоиться о читаемости в данном случае не Ваша забота, а забота автора библиотеки, не Вы же ее поддерживаете.
Sign up to leave a comment.

Articles