Comments 15
Хотел бы уточнить две вещи:
1. Для чего создавать доп. файлы, но без расширения, например sstream и sstream.h?
2. Почему именно C++11, а не C++17, Там ведь добавили новые функции и т.д.?
1. Для чего создавать доп. файлы, но без расширения, например sstream и sstream.h?
2. Почему именно C++11, а не C++17, Там ведь добавили новые функции и т.д.?
1. Я хотел включить заголовочные файлы с теми же именами что и в стандартной библиотеке, но опять из-за багов компиляторов все пошло не так. Некоторые компиляторы считают что если в директиву #include включается файл без расширения, но при этом у него прописан путь, то значит ему нужно «дописать» .h в конце и искать его таким образом. Видимо по той логике, что файлы без расширения могут быть только от стандартной библиотеки, а значит путей не содержат.
Пример (файла type_traits нет в стандартной библиотеке, это заголовочный файл от моей библиотеки stdex):
Пришлось написать все заголовочные файлы с расширением .hpp и стандартными именами, при этом написав по сути обертки без расширения, которые просто включают в себя аналогичный файл .hpp.
2. Потому что нужен был хотя бы C++ 11. И то не в полном объеме. Я реализовал что нужно было по минимуму, но посчитал что не дело оставлять реализацию на пол пути, да и увлекся я уже. Так что сейчас стараюсь нагнать хотя бы этот стандарт, а до последующих добраться уже имея реализацию C++ 11 будет проще намного.
Пример (файла type_traits нет в стандартной библиотеке, это заголовочный файл от моей библиотеки stdex):
stdex расположена в «C:\my_code\stdex\», путь до type_traits будет «C:\my_code\stdex\type_traits». Тогда в компиляторе:
include path = «C:\my_code\stdex\»
#include <type_traits> // OK
include path = «C:\my_code\»
#include <stdex/type_traits> // compilation error "can not include 'stdex/type_traits.h': no such file or directory" #include <stdex/type_traits.hpp> // приходится использовать так
Пришлось написать все заголовочные файлы с расширением .hpp и стандартными именами, при этом написав по сути обертки без расширения, которые просто включают в себя аналогичный файл .hpp.
2. Потому что нужен был хотя бы C++ 11. И то не в полном объеме. Я реализовал что нужно было по минимуму, но посчитал что не дело оставлять реализацию на пол пути, да и увлекся я уже. Так что сейчас стараюсь нагнать хотя бы этот стандарт, а до последующих добраться уже имея реализацию C++ 11 будет проще намного.
Внимательный читатель уже говорил, что надо делать. В прошлый раз. :) Вот, собственно, как можно отличить enum от int'а:
ideone.com/hqevaJ
В данном случае ручное (явное) перечисление целочисленных типов играет сильно на руку — подавляет компиляторную магию.
#include <iostream>
struct true_type
{
enum {value = 1};
};
struct false_type
{
enum {value = 0};
};
template<typename T>
struct is_integer_impl : false_type {};
#define DECLARE_INTEGER(T) \
template<> \
struct is_integer_impl<T> : true_type {}
DECLARE_INTEGER(char);
DECLARE_INTEGER(unsigned char);
DECLARE_INTEGER(signed char);
DECLARE_INTEGER(unsigned short);
DECLARE_INTEGER(signed short);
DECLARE_INTEGER(unsigned int);
DECLARE_INTEGER(signed int);
DECLARE_INTEGER(unsigned long);
DECLARE_INTEGER(signed long);
template<typename T>
struct is_integer : is_integer_impl<T> {};
template<typename T>
struct is_class
{
typedef char yes;
typedef int no;
template<class U>
static yes doCheck(void (U::*)());
template<class U>
static no doCheck(...);
enum {value = (sizeof(doCheck<T>(0)) == sizeof(yes))};
};
template<typename T>
struct is_enum
{
enum {value = !is_integer<T>::value && !is_class<T>::value};
};
enum TestEnum
{
Item1,
Item2
};
struct TestStruct
{
};
int main()
{
std::cout << is_integer<int>::value << std::endl;
std::cout << is_enum<int>::value << std::endl;
std::cout << is_class<int>::value << std::endl;
std::cout << std::endl;
std::cout << is_integer<TestStruct>::value << std::endl;
std::cout << is_enum<TestStruct>::value << std::endl;
std::cout << is_class<TestStruct>::value << std::endl;
std::cout << std::endl;
std::cout << is_integer<TestEnum>::value << std::endl;
std::cout << is_enum<TestEnum>::value << std::endl;
std::cout << is_class<TestEnum>::value << std::endl;
return 0;
}
ideone.com/hqevaJ
В данном случае ручное (явное) перечисление целочисленных типов играет сильно на руку — подавляет компиляторную магию.
1. Внимательный читатель заметит что is_class определен через множество проверок не просто так (и в том числе зависит от is_enum если бы он был). =)
2.
Я думаю что можно как то хитро использовать свойство enum конвертироваться в int, или отсутствие конструторов/деструкторов/функций-членов класса. Но пока что к реализации этого не дошел, тестирую.
2.
is_class<EnumType>::value
будет true на некоторых компиляторах, т.к. по сути им без разницы что за контейнер. Если он не встроенный тип, то он по умолчанию считается имеющим member pointer. Я думаю что можно как то хитро использовать свойство enum конвертироваться в int, или отсутствие конструторов/деструкторов/функций-членов класса. Но пока что к реализации этого не дошел, тестирую.
Ну, такой вариант is_class у нас успешно работает на довольно широкой линейке компиляторов — gcc начиная с 3.9, MSVC начиная с 2008, clang и некоторая экзотика. То есть я допускаю, что существуют компиляторы, которые поведут себя в этом случае иначе, но, к счастью, давно ими не приходилось пользоваться.
Борландовские версии к сожалению не работают, сегодня проверил :) Я долго пытался состряпать workaround, но похоже пациент совсем плохо умеет SFINAE — ничего не вышло.
Если автору важно поддерживать Borland C++, то вполне понятно почему он не применил это решение.
Если автору важно поддерживать Borland C++, то вполне понятно почему он не применил это решение.
… проблема возникает с неполным типом T[] (массив без указания длины). Дело в том что данный тип не определяется некоторыми компиляторами (C++ Builder) при специализации шаблона, и универсальное решение здесь я пока что не нашел.
Проверил код ниже на C++ Builder 6 — работает.
namespace detail {
template <typename T>
static yes_type foo(T (*)[]);
static no_type foo(...);
template <typename T>
static T * declptr();
}
// is_array
template <class Tp>
struct is_array
{
enum
{
value = sizeof(detail::foo(detail::declptr<Tp>())) == sizeof(yes_type)
};
};
template <class Tp, std::size_t Size>
struct is_array<Tp[Size]>
: true_type
{ };
Это противоречит стандарту. Точнее теперь уже не противоречит с введением C++ 17, но до этого указатели на безразмерные массивы запрещены были. Они разрешены в чистом C, но в C++ их решили не переносить, т.к. использование их в кодовой базе на тот момент было очень ограничено, а поддержка для разработчиков компиляторов была слишком сложной.
На SO есть как раз вопрос с отличным ответом на этот счет.
Выдержка от туда:
Вопрос про функцию принимающую указатель на массив неизвестной длины и почему она не компилируется g++
Ответ
На SO есть как раз вопрос с отличным ответом на этот счет.
Выдержка от туда:
Вопрос про функцию принимающую указатель на массив неизвестной длины и почему она не компилируется g++
int accumulate(int n, const int (*array)[])
Ответ
The committees decided that functions such as this, that accept a pointer or reference to an array with unknown bound, complicate declaration matching and overload resolution rules in C++. The committees agreed that, since such functions have little utility and are fairly uncommon, it would be simplest to just ban them. Hence, the C++ draft now states:
If the type of a parameter includes a type of the form pointer to array of unknown bound of T or reference to array of unknown bound of T, the program is ill-formed.
Таки шашечки или ехать? :)
В бусте тоже встречаются workaround`ы с нестандартными особенностями, обложенные проверками под конкретные компиляторы.
Ведь если мы работаем в условиях такой плохой поддержки стандарта, то сетовать на несоответствие стандарту некоторым образом лукавство, особенно, если код обеспечивает требуемое поведение.
Поэтому тут надо выбирать что важнее.
PS. Да, и я в курсе этих особенностей, т.к. сам некоторым образом решал подобную вашей задачу (пример: habr.com/post/277727), но для линейки старых GCC и Intel под Linux и BSD. Попробуйте вашу реализацию на GCC 2.x, возможно найдется множество интересных локальных задачек по нахождению обходных путей.
В бусте тоже встречаются workaround`ы с нестандартными особенностями, обложенные проверками под конкретные компиляторы.
Ведь если мы работаем в условиях такой плохой поддержки стандарта, то сетовать на несоответствие стандарту некоторым образом лукавство, особенно, если код обеспечивает требуемое поведение.
Поэтому тут надо выбирать что важнее.
PS. Да, и я в курсе этих особенностей, т.к. сам некоторым образом решал подобную вашей задачу (пример: habr.com/post/277727), но для линейки старых GCC и Intel под Linux и BSD. Попробуйте вашу реализацию на GCC 2.x, возможно найдется множество интересных локальных задачек по нахождению обходных путей.
Довольно иронично то, что именно на этот вопрос я и отвечал в предыдущей статье про nullptr =)
Если вкратце говорить о подходе в этой библиотеке — там нет ни одного отхода от стандарта, код не полагается на неопределенное поведение и на особенности компилятора по реализации этого поведения. А все баги в реализации стандарта на разных компиляторах приводятся, за счет шаблонной условной компиляции, к общему виду. Не хочется писать еще один boost с макросами под каждый компилятор и его версию, и цели такой не было.
P.S.: я именно вашу статью тоже с удовольствием давно уже прочел и плюс поставил. Это действительно очень занимательно, но у меня про другое немного.
Если вкратце говорить о подходе в этой библиотеке — там нет ни одного отхода от стандарта, код не полагается на неопределенное поведение и на особенности компилятора по реализации этого поведения. А все баги в реализации стандарта на разных компиляторах приводятся, за счет шаблонной условной компиляции, к общему виду. Не хочется писать еще один boost с макросами под каждый компилятор и его версию, и цели такой не было.
P.S.: я именно вашу статью тоже с удовольствием давно уже прочел и плюс поставил. Это действительно очень занимательно, но у меня про другое немного.
Сама статья про другое, но написана она была по мотивам создания практически такого же набора инструментов, как у вас. Платформа только различается и целевые компиляторы.
Вы не подумайте, мне очень близка ваша идея строгого соответствия «букве» закона. Но я, как практик, при столкновении с подобными трудностями в своей реализации, делая выбор между «поддерживать фичу с оговорками» или «не поддерживать вовсе» выбираю первое :)
Взять, например, задачу определения наличия функции-члена класса с заданной сигнатурой, которая нерешаема в рамках стандартного С++98. Однако, эта возможность была необходима в инструментарии, который я создавал. Без нее было бы слишком неудобно им пользоваться. Поэтому я пошел на этот компромисс.
Также приходилось сталкиваться в ситуацией, когда невозможно написать общий код для всей линейки нужных компиляторов, и таки приходилось делать условную компиляцию. Т.к. баги-фичи одних компиляторов были взаимоисключающими относительно других. Общий знаменатель был невозможен.
Вы не подумайте, мне очень близка ваша идея строгого соответствия «букве» закона. Но я, как практик, при столкновении с подобными трудностями в своей реализации, делая выбор между «поддерживать фичу с оговорками» или «не поддерживать вовсе» выбираю первое :)
Взять, например, задачу определения наличия функции-члена класса с заданной сигнатурой, которая нерешаема в рамках стандартного С++98. Однако, эта возможность была необходима в инструментарии, который я создавал. Без нее было бы слишком неудобно им пользоваться. Поэтому я пошел на этот компромисс.
Также приходилось сталкиваться в ситуацией, когда невозможно написать общий код для всей линейки нужных компиляторов, и таки приходилось делать условную компиляцию. Т.к. баги-фичи одних компиляторов были взаимоисключающими относительно других. Общий знаменатель был невозможен.
Кстати, очень интересно было бы посмотреть на вашу реализацию common_type.
Могу сказать, что в моем случае — это как раз один из тех примеров, когда пришлось делать условную компиляцию. Для GCC 2.х реализация оказалась настолько нетривиальна, что брать ее как основную мне не позволила совесть.
Могу сказать, что в моем случае — это как раз один из тех примеров, когда пришлось делать условную компиляцию. Для GCC 2.х реализация оказалась настолько нетривиальна, что брать ее как основную мне не позволила совесть.
imho. А почему бы не назвать namespace stdx (по антологии xhtml (extertion html))? Так было бы, на мой взгляд, красивше, короче, да и почему и нет?
P.S. Чисто, ради интереса
P.S. Чисто, ради интереса
Sign up to leave a comment.
Как я стандартную библиотеку C++11 писал или почему boost такой страшный. Глава 4.3