Показаны сообщения с ярлыком visual studio. Показать все сообщения
Показаны сообщения с ярлыком visual studio. Показать все сообщения

среда, 15 июня 2016 г.

Чёртова студия (13) и указатели на методы

Несколько дней прободался с падением юнит теста при сборке его 13 студией (gcc, clang, msvc2015 - всё ок). Фишка оказалась вот в чём.

Чтобы не писать многа апсалютна непанятных букаф я завёл вспомогательный шаблон

template<typename C, typename T>
using member_ptr = T C::*;
Фишка в том, что если в качестве типа использовать сигнатуру функции, то мы получим указатель на метод, например
member_ptr<std::string, void(size_t)> reserve =
    &std::string::reserve;
std::string val;
val.*reserve(5);
Шаблон дичайше удобен, но! 13 студия считает, что у такого указателя на функцию тип будет "указатель на функцию член с конвенцией вызова __cdecl" (не очень разбираюсь в виндовых конвенциях вызова, но кажется что для нестатической функции члена это бессмыслица). С этой самой точки всё и отправляется в гости к маленькому пушному зверьку с ценным мехом ибо вызов через такой указатель на метод будет использовать неправильное ABI передачи параметров.

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

вторник, 31 мая 2016 г.

Сложные type_trait'ы старые студии и свежие gcc

Продолжая баловаться со static_reflection захотел уметь проверять возможно ли проитерироваться по заданному типу используя C++11 range-for. Попытавшись написать вот так:

template<typename T, typename = void_t<>>
struct is_iterable: false_type {};

template<typename T>
struct is_iterable<
  T, void_t<
    decltype(for (const auto& i: declval<const T&>()) {})
  >
>: true_type {};
я разумеется сразу же понял, что Expression SFINAE неприменим для проверки правильности того, что является Statement с точки зрения языка C++. Опечалившись этим фактом я решил воспользоваться наличием под рукой GCC 6.1 известного тем, что это единственный в мире компилятор с поддержкой концептов. И так родился следующий код:
template<typename T>
concept bool Iterable = requires(const T& t) {
  {for (const auto& item: t) {}};
};
который был безбожно забрит компилятором. Чтение спецификации Concepts Light TS подтвердило правоту компилятора, но пошатнуло мою веру в разумное, доброе, вечное. Оказывается requires позволяет проверять возможность использования своих операндов в выражениях (expressions), а в операциях (statement) не позволяет.

Всё ещё опечаленный сим фактом я вернулся к имеющемуся у меня в боевых условиях GCC 4.9 написал работающую проверку использую многа ненужных букаф:

using std::begin;
using std::end;

template<typename T, typename = void_t<>>
struct is_iterable: false_type {};

template<typename T>
struct is_iterable<
  T,
  void_t<
    decltype(begin(declval<const T&>())),
    decltype(end(declval<const T&>())),
    decltype(begin(declval<const T&>()) != end(declval<const T&>())),
    decltype(++begin(declval<const T&>())),
    decltype(*begin(declval<const T&>()))
  >
>: true_type {};
И пошёл компилировать всё это дело в стуиях. 15 это всё съела, чем меня несказанно порадовала. Это впервые на моей памяти, когда компилятор от мелкомягких просто и спокойно скомпилировал нетривиальный шаблон отлаженный на GCC с CLANG'ом.

Но из за WinPhone8 я был обречён на подлый удар в спину от студии 13ой. Гугл, stack-overflow и два дня экспериментов позволили найти выход. Который уже работает везде:

using std::begin;
using std::end;

template<typename T>
auto test_begin(const T& t) -> decltype(begin(t));
auto test_begin(...) -> false_type;

template<typename T>
auto test_end(const T& t) -> decltype(end(t));
auto test_end(...) -> false_type;

template<typename T>
auto test_it_noteq_end(const T& t) -> decltype(begin(t) != end(t));
auto test_it_noteq_end(...) -> false_type;

template<typename T>
auto test_it_increment(const T& t) -> decltype(++begin(t));
auto test_it_increment(...) -> false_type;

template<typename T>
auto test_it_deref(const T& t) -> decltype(*begin(t));
auto test_it_deref(...) -> false_type;

template<typename T>
using is_iterable = typename conditional<
  (
    !is_same<decltype(test_begin(declval<const T&>())), false_type>::value &&
    !is_same<decltype(test_end(declval<const T&>())), false_type>::value &&
    !is_same<decltype(test_it_noteq_end(declval<const T&>())), false_type>::value &&
    !is_same<decltype(test_it_increment(declval<const T&>())), false_type>::value &&
    !is_same<decltype(test_it_deref(declval<const T&>())), false_type>::value
  ),
  true_type,
  false_type
>::type;

И всё же я безумно хочу, чтобы можно было написать вот так:

template<typename T>
concept bool Iterable = requires(const T& t) {
  {for (const auto& item: t) {}};
};
ибо понять такой код неправильно много сложней чем понять правильно финальную версию рабочей реализации моего трейта.