std::string password, login;
unsigned short TCPport, YearOfBirth;
Компилятор запрещает бессмысленные присвоения:
login = TCPport; // error
Однако не менее бессмысленные:
TCPport = YearOfBirth; // ok
запретить не может.
Писать класс для каждого подобного типа не реально.
Возникла идея написать шаблон прокси класса:
template< class ContainType >class UniqueType{
ContainType v;
public:
UniqueType( ContainType c ): v( c ){}
operator ContainType(){ return v; }
// При необходимости перегружаем еще что-нибудь.
};
Вопрос: как обеспечить уникальность типа?
Хочется чтоб выглядело приблизительно так:
В этом сценарии есть одна загвоздка — здесь одной только уникальностью не обойтись т.к. сконструировать можно из чего угодно, примерно так:
UniqueType< std::string > email = to_string(password); // Хотя email можно было бы проверить на валидность.
То же самое с присваиванием 'TCPport = YearOfBirth'. Поэтому мне больше импонирует такой подход — проверки(ака policy) можно вынести в тот же тег и обернуть, например так:
Проверка происходит в конструкторе, поэтому создание инвалидного объекта невозможно (выполняется name_policy::operator()), плюс экземпляр checked_value можно изменить только одним путем — присвоением другого объекта checked_value, + вспомогательные-операторы по необходимости...
Здравствуйте, Murom, Вы писали:
M>Здравствуйте, rs4i, Вы писали:
R>>Вопрос: как обеспечить уникальность типа? R>>Хочется чтоб выглядело приблизительно так: R>>
M>У нас в coding-style, например, запрещено определять несколько переменных на одной строке. M>Так нам вообще без присваиваний писать?
Не понял. Кто это мы? Где у меня в коде определения нескольких переменных на одной строке?
Как раз, писать желательно с присвоением.
Я вообще не встречал ни одного случая чтобы нужно было объявить но не инициализировать переменную.
К сожалению, у нас (контора где работаю) не прописан в виде дока корпоративный стиль кодирования, поэтому я не четко его себе представляю.
Если не трудно, киньте пару ссылок или образец нормативных документов. В порядке обмена опытом, так сказать ..
> В этом сценарии есть одна загвоздка — здесь одной только уникальностью не обойтись
Рантайм проверки — отдельная тема.
Мне нужно чтобы, скажем, килограммы картошки, нельзя было присвоить километрам пути, на этапе компиляции.
Странные преобразования типов и тд хак способы запрещать не собираюсь.
Если пользователь лучше меня знает что нужно делать, так и флаг ему в руки.
Мне нужна защита от случайных или глупых ошибок. > Теги какие-нибудь
Это первое что пришло мне в голову.
template< class ContainType, class TypeName >class UniqueType{
ContainType v;
public:
UniqueType( ContainType c ): v( c ){}
operator ContainType(){ return v; }
};
#define TypedefUniqueType( ContainType, TypeName )\
struct MACRO__##TypeName{};\
typedef UniqueType< ContainType, MACRO__##TypeName > TypeName
TypedefUniqueType( std::string, PasswordType );
PasswordType password; // уникальный тип
TypedefUniqueType( std::string, LoginType );
LoginType login; // уникальный тип
login = password; // error
Но, согласитесь, это как-то криво.
Мне нужно в одной строке объявить и инициализировать переменную,
и, по возможности, обойтись без макросов.
Вот эту композицию, я просто не понял:
template<class type_>
struct tag { typedef type_ type; };
struct name : tag<std::string> {};
Может просто туплю после работы?
Завтра постараюсь осмыслить.
А вообще я смотрю, эта тема вам знакома, и уже проработана.
Пожалуйста, не теряйтесь из виду. Я бы хотел еще чуть-чуть пообщаться.
Здравствуйте, rs4i, Вы писали:
R>Здравствуйте, Murom, Вы писали:
M>>Здравствуйте, rs4i, Вы писали:
R>>>Вопрос: как обеспечить уникальность типа? R>>>Хочется чтоб выглядело приблизительно так: R>>>
M>>У нас в coding-style, например, запрещено определять несколько переменных на одной строке. M>>Так нам вообще без присваиваний писать?
R>Не понял. Кто это мы? Где у меня в коде определения нескольких переменных на одной строке?
Ты не понял в чем вопрос. Если ты хочешь завести две совместимые переменные ОДНОГО типа UniqueType<std::string>, например, oldpassword и newpassword, то как ты это сделаешь в твоем примере?
Здравствуйте, rs4i, Вы писали:
R>Мне нужно в одной строке объявить и инициализировать переменную, R>и, по возможности, обойтись без макросов.
Можно не усложнять:
template<typename ContainType_, typename TagType_>
class UniqueType {
public:
UniqueType(const ContainType_ &rhs) : val_(rhs){}
// ...
ContainType_ val_;
}; // UniqueType<>typedef UniqueType<std::string, class MyTypeName_> MyType;
MyType myVar("text");
UniqueType<std::string, class Password_> pwd("secret password"); // Объявление типа и инициализация
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, rs4i, Вы писали:
>> В этом сценарии есть одна загвоздка — здесь одной только уникальностью не обойтись R>Рантайм проверки — отдельная тема. R>Мне нужно чтобы, скажем, килограммы картошки, нельзя было присвоить километрам пути, на этапе компиляции.
Килограммы и километры у меня сразу ассоциируются с зоопарком единиц измерений и действиях/переводах/etc. между ними (dimensional analysis). Это более обширная тема, чем просто возможность/невозможность присваивания, примеры есть в Boost.Units.
R>Странные преобразования типов и тд хак способы запрещать не собираюсь. R>Если пользователь лучше меня знает что нужно делать, так и флаг ему в руки. R>Мне нужна защита от случайных или глупых ошибок.
Если контракты соответствующих типов одинаковы (как минимум, гипотетические функции is_valid_login и is_valid_password эквивалентны), то я не вижу контекстов, в которых имеет смысл делать такие типы 'разными'.
Конкретно вот эта ошибка:
login = password;
автоматически приведет к невыполнению постусловий (функции).
Попытки сделать такое окружение в котором такой(и весь подобный) код не cкомпилируется (тотальная 'защита от дурака'), хоть и являются благими намереньями, но в С++ не особо(имхо) оправдывают себя. Для меня смысл такой ошибки эквивалентен:
int b = //...int c = //...int a = b - c; // ошибка, должно быть 'b + с'
Можно конечно, вместо типа 'int' для объекта 'b' сделать свой тип и разрешить только 'operator+', но...
R>Мне нужно в одной строке объявить и инициализировать переменную, R>и, по возможности, обойтись без макросов.
Т.е. такой тип должен быть локальным в блоке, где используется?
template<class T, int>
class UniqueType{ /**/ };
//...
UniqueType<std::string, 0> login = ...
UniqueType<std::string, 1> pass = ...
// или так
UniqueType<std::string, __LINE__> login = ...
Если использовать локально, то подойдет.
R>Вот эту композицию, я просто не понял: R>template<class type_> R>struct tag { typedef type_ type; }; R>struct name : tag<std::string> {};
Не дописал пару строк. Этим предполагается инстанциировать
Здравствуйте, Alexey F, Вы писали:
AF>Кажется, инстанцирование неполными типами приводит к ub... Или тут какое-то хитрое исключение?
А разве это не для типов из STL так?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>А разве это не для типов из STL так?
Точно, спасибо. Вспомнил про:
17.4.3.6 Other functions
2 In particular, the effects are undefined in the following cases:
— if an incomplete type (3.9) is used as a template argument when instantiating a template component.
и совсем забыл, что глава 17 называется "Library introduction"
Здравствуйте, Alexey F, Вы писали:
AF>Здравствуйте, Erop, Вы писали:
E>>А разве это не для типов из STL так? AF>Точно, спасибо. Вспомнил про: AF>
AF>17.4.3.6 Other functions
AF>2 In particular, the effects are undefined in the following cases:
AF>— if an incomplete type (3.9) is used as a template argument when instantiating a template component.
AF>и совсем забыл, что глава 17 называется "Library introduction"
Здесь еще другой аспект есть:
3.3.1/5 +
14.3.1/2
A local type, a type with no linkage, an unnamed type or a type compounded from any of these types shall
not be used as a template-argument for a template type-parameter
UniqueType<std::string, class Password_1> pwd("secret password"); // okvoid f()
{
UniqueType<std::string, class Password_2> pwd("secret password"); //error
}
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>Здесь еще другой аспект есть: ЮЖ>3.3.1/5 + ЮЖ>
14.3.1/2
ЮЖ>A local type, a type with no linkage, an unnamed type or a type compounded from any of these types shall
ЮЖ>not be used as a template-argument for a template type-parameter
ЮЖ>
ЮЖ>UniqueType<std::string, class Password_1> pwd("secret password"); // ok
ЮЖ>void f()
ЮЖ>{
ЮЖ> UniqueType<std::string, class Password_2> pwd("secret password"); //error
ЮЖ>}
Да, согласен, именно такая конструкция допустима во фривольном VC++ (он локальными типами инстанцировать позволяет). Comeau ругается.
Что касается неполноты, так теговый тип не используется внутри UniqueType, он нужен только как дополнительное имя в сигнатуре шаблона. ИМХО, тут как раз поведение того же VC вполне логично — если тип никак не используется, то не должно быть и заморочек при линковке.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Геннадий Васильев, Вы писали:
... ГВ>Да, согласен, именно такая конструкция допустима во фривольном VC++ (он локальными типами инстанцировать позволяет). Comeau ругается.
VC++ с '/Za' должен(под рукой нет) ругаться.
Defect report #488 отменяет ограничение на инстанцирование локальными/безымянными типами, поэтому согласно будущему стандарту такое использование вполне законно. Пример оттуда:
template <class T> class X { };
template <class T> void f(T t) { }
struct { } unnamed_obj;
void f() {
struct A { };
enum { e1 };
typedef struct { } B;
B b;
X<A> x1; // OK
X<A*> x2; // OK
X<B> x3; // OK
f(e1); // OK
f(unnamed_obj); // OK
f(b); // OK
}
ГВ>Что касается неполноты, так теговый тип не используется внутри UniqueType, он нужен только как дополнительное имя в сигнатуре шаблона. ИМХО, тут как раз поведение того же VC вполне логично — если тип никак не используется, то не должно быть и заморочек при линковке.
Согласен, (не)полнота здесь роли не играет, такой ведь пример:
template<class> struct X{};
int main()
{
X<void> v;
}
Здравствуйте, Alexey F, Вы писали:
AF>Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>>
AF>[...skip...]
ГВ>>UniqueType<std::string, class Password_> pwd("secret password"); // Объявление типа и инициализация
ГВ>>
AF>А выделенное — легально с точки зрения стандарта?
Не совсем. Но, в свою очередь, Incomplete Types довольно противоречиво описаны в стандарте.
AF>Кажется, инстанцирование неполными типами приводит к ub... Или тут какое-то хитрое исключение?
Да, правильно. И кроме того, Юрий Жмеренецкий чуть ниже правильно вспомнил 14.3.1. Но в общем, складывается впечатление, что 14.3.1 носит скорее рекомендательный, чем предписывающий характер. Откуда бы тогда взялось 17.4.3.6? Получается, что Incomplete Type использовать в качестве аргумента шаблона нельзя, но если уж использован (как так, если нельзя?), то будет UB.
Поэтому я тут пытаюсь руководствоваться соображениями здравого смысла: если Incomplete Type используется только в аргументах шаблона (мы не создаём экземпляров IT и не обращаемся к его членам), то, по идее, хоть это и UB, но вполне предсказуемое — тип должен быть создан в той же области видимости, что и инстанцируемый шаблон, и в итоге должно сохраниться только его имя (выравнивания, списки членов не являются частями идентификаторов классов C++). То есть конструкция:
template<typename IncompleteOne_> class X { ... };
namespace ns1 {
X<class Z_> xz;
}
Должна привести к появлению примерно такого имени типа: ns1:: class ::X < class ns1::Z_ >. Больше ничего значимого сюда воткнуто быть не может по определению. Для линковщика такая штука тоже не должна стать помехой (ns1::Z_ никак не используется).
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ГВ>>Да, согласен, именно такая конструкция допустима во фривольном VC++ (он локальными типами инстанцировать позволяет). Comeau ругается. ЮЖ>VC++ с '/Za' должен(под рукой нет) ругаться.
Да, правильно, только использование хидеров windows плотно ассоциируется с отключённой /Za. А так, конечно.
ЮЖ>Defect report #488 отменяет ограничение на инстанцирование локальными/безымянными типами, поэтому согласно будущему стандарту такое использование вполне законно. Пример оттуда:
[skip]
Ах вот оно, в чём дело. Надо внимательней посмотреть на C++0x.
Но, кстати, по-моему, как раз тот случай, когда MS-овское "нарушение конвенций" вполне оправдано. В прочем, это уже офтоп.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Здравствуйте, rs4i, Вы писали:
R>>Мне нужно в одной строке объявить и инициализировать переменную, R>>и, по возможности, обойтись без макросов.
ГВ>Можно не усложнять:
...
Но с практической точки зрения тег можно нагрузить еще и полезной метаинформацией, тогда запись типа станет лаконичной, а код более читаемый:
// тег для логинаstruct Login
{
typedef std::string Type;
};
// тег для пароляstruct Password
{
typedef std::string Type;
};
template<typename T>
class UniqueType
{
public:
typedef typename T::Type Type;
explicit UniqueType(const T &value) : value(value) {}
...
private:
Type value;
};
...
UniqueType<Login> login("User");
UniqueType<Password> password("***");
Здравствуйте, Анатолий Широков, Вы писали:
АШ>Но с практической точки зрения тег можно нагрузить еще и полезной метаинформацией, тогда запись типа станет лаконичной, а код более читаемый:
[skip]
Не буду спорить: такой подход может оказаться более предпочтительным в каких-то ситуациях. Например, в тег можно поместить упомянутые уже здесь проверки ограничений и т.д. Я, правда, больше сторонник комбинирования свободных функций и очень мелких классов, которые можно привязать к идентификаторам типов. ИМХО, так гибче получается. Но, повторюсь, тут скорее дело вкуса.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Анатолий Широков, Вы писали:
АШ>Но с практической точки зрения тег можно нагрузить еще и полезной метаинформацией, тогда запись типа станет лаконичной, а код более читаемый:
АШ>
Так, выглядит значительно лучше.
Но желательно описать переменную одной строкой, и там же ее инициализировать.
На каждый новый тип нужен новый тег, а это несколько лишних строк.
Ну да. Я упустил это из вида.
Тег можно не просто как имя использовать,
но и нагрузить дополнительной полезной информицией.
Например базовый тип.
Можно туда же поместить и предикат для рантайм проверки.
В случае с std::string например, регулярное выражение.
Это где-нибудь уже реализовано?
Здравствуйте, vadimcher, Вы писали: V>Ты не понял в чем вопрос. Если ты хочешь завести две совместимые переменные ОДНОГО типа UniqueType<std::string>, например, oldpassword и newpassword, то как ты это сделаешь в твоем примере?
Как то так:
typedef UniqueType< std::string > Password;
Password oldpassword;
Password newpassword;
Можно поместить информацио о типе в сам шаблон:
UniqueType< std::string > oldpassword;
oldpassword::Type newpassword;
Здравствуйте, rs4i, Вы писали:
R>UniqueType< std::string > oldpassword; R>oldpassword::Type newpassword;
Это как?
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, rs4i, Вы писали:
... R>Можно туда же поместить и предикат для рантайм проверки. R>В случае с std::string например, регулярное выражение. R>Это где-нибудь уже реализовано?
Здравствуйте, frogkiller, Вы писали:
F>Здравствуйте, rs4i, Вы писали:
R>>Вопрос: как обеспечить уникальность типа?
F>Не так давно тут была забавная тема про compile-time счётчик
Я в общем-то в этом направлении и копал, пока не убедился что это сделать нельзя.
(смотрел ликбез по лиспу)
Счетчик интересный получился, только не везде рабочий.
На VC9 считает до 498, на GCC не работает