Реализация идеомы UniqueType
От: rs4i  
Дата: 27.06.09 05:25
Оценка:
Идея проста и стоит во главе идеологии C++:
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 > password = "******"; // уникальный тип
UniqueType< std::string > login = "rs4i"; // уникальный тип
password = login; // error
Re: Реализация идеомы UniqueType
От: Sni4ok  
Дата: 27.06.09 07:25
Оценка:
Здравствуйте, rs4i

велосипед изобретаете, посмотрите на макрос BOOST_STRONG_TYPEDEF из буста
Re: Реализация идеомы UniqueType
От: Юрий Жмеренецкий ICQ 380412032
Дата: 27.06.09 07:38
Оценка:
Здравствуйте, rs4i, Вы писали:

R>Идея проста и стоит во главе идеологии C++:

R>
R>std::string password, login;
R>unsigned short TCPport, YearOfBirth;
R>

R>Компилятор запрещает бессмысленные присвоения:
R>
R>login = TCPport; // error
R>

R>Однако не менее бессмысленные:
R>
R>TCPport = YearOfBirth; // ok
R>

R>запретить не может.
R>Писать класс для каждого подобного типа не реально.
R>Возникла идея написать шаблон прокси класса:
R>
R>template< class ContainType >class UniqueType{
R>  ContainType v;
R>public:
R>  UniqueType( ContainType c ): v( c ){}
R>  operator ContainType(){ return v; }
R>  // При необходимости перегружаем еще что-нибудь.
R>};
R>

R>Вопрос: как обеспечить уникальность типа?
Теги какие-нибудь:
template< class ContainType , class Tag >class UniqueType //...

struct name_tag;
typedef UniqueType<std::string, name_tag> name_type;


Или как-нибудь так:
template<class type_>
struct tag { typedef type_ type; };

struct name : tag<std::string> {};

В бусте еще есть BOOST_STRONG_TYPEDEF (для примитивных типов)

R>Хочется чтоб выглядело приблизительно так:

R>
R>UniqueType< std::string > password = "******"; // уникальный тип
R>UniqueType< std::string > login = "rs4i"; // уникальный тип
R>password = login; // error
R>

В этом сценарии есть одна загвоздка — здесь одной только уникальностью не обойтись т.к. сконструировать можно из чего угодно, примерно так:
UniqueType< std::string > email = to_string(password); // Хотя email можно было бы проверить на валидность.

То же самое с присваиванием 'TCPport = YearOfBirth'. Поэтому мне больше импонирует такой подход — проверки(ака policy) можно вынести в тот же тег и обернуть, например так:
struct name_policy
{
  void operator()(const std::string& name) const
  {
    if(name.empty())
      throw invalid_name(empty);
            
    if(is_reserved_name(name))
      throw invalid_name(reserved);
            
    ///...
};

typedef checked_value<std::string, name_policy> name;


Проверка происходит в конструкторе, поэтому создание инвалидного объекта невозможно (выполняется name_policy::operator()), плюс экземпляр checked_value можно изменить только одним путем — присвоением другого объекта checked_value, + вспомогательные-операторы по необходимости...
Re: Реализация идеомы UniqueType
От: Murom Россия  
Дата: 27.06.09 11:25
Оценка:
Здравствуйте, rs4i, Вы писали:

R>Вопрос: как обеспечить уникальность типа?

R>Хочется чтоб выглядело приблизительно так:
R>
R>UniqueType< std::string > password = "******"; // уникальный тип
R>UniqueType< std::string > login = "rs4i"; // уникальный тип
R>password = login; // error
R>


У нас в coding-style, например, запрещено определять несколько переменных на одной строке.
Так нам вообще без присваиваний писать?
- Eugeny
Re[2]: Реализация идеомы UniqueType
От: rs4i  
Дата: 27.06.09 18:50
Оценка:
Здравствуйте, Murom, Вы писали:

M>Здравствуйте, rs4i, Вы писали:


R>>Вопрос: как обеспечить уникальность типа?

R>>Хочется чтоб выглядело приблизительно так:
R>>
R>>UniqueType< std::string > password = "******"; // уникальный тип
R>>UniqueType< std::string > login = "rs4i"; // уникальный тип
R>>password = login; // error
R>>


M>У нас в coding-style, например, запрещено определять несколько переменных на одной строке.

M>Так нам вообще без присваиваний писать?

Не понял. Кто это мы? Где у меня в коде определения нескольких переменных на одной строке?

Как раз, писать желательно с присвоением.
Я вообще не встречал ни одного случая чтобы нужно было объявить но не инициализировать переменную.
К сожалению, у нас (контора где работаю) не прописан в виде дока корпоративный стиль кодирования, поэтому я не четко его себе представляю.
Если не трудно, киньте пару ссылок или образец нормативных документов. В порядке обмена опытом, так сказать ..
Re[2]: Реализация идеомы UniqueType
От: rs4i  
Дата: 27.06.09 18:59
Оценка:
Здравствуйте, Sni4ok, Вы писали:

S>Здравствуйте, rs4i


S>велосипед изобретаете, посмотрите на макрос BOOST_STRONG_TYPEDEF из буста


Спасибо. Если в асортименте нет велосипедов,
то приходиться его делать вручную.
Я просто с этим макросом не сталкивался.
Будем смотреть ..
Re[2]: Реализация идеомы UniqueType
От: rs4i  
Дата: 27.06.09 19:28
Оценка:
> В этом сценарии есть одна загвоздка — здесь одной только уникальностью не обойтись
Рантайм проверки — отдельная тема.
Мне нужно чтобы, скажем, килограммы картошки, нельзя было присвоить километрам пути, на этапе компиляции.
Странные преобразования типов и тд хак способы запрещать не собираюсь.
Если пользователь лучше меня знает что нужно делать, так и флаг ему в руки.
Мне нужна защита от случайных или глупых ошибок.
> Теги какие-нибудь
Это первое что пришло мне в голову.
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> {};
Может просто туплю после работы?
Завтра постараюсь осмыслить.

А вообще я смотрю, эта тема вам знакома, и уже проработана.
Пожалуйста, не теряйтесь из виду. Я бы хотел еще чуть-чуть пообщаться.
Re[3]: Реализация идеомы UniqueType
От: vadimcher  
Дата: 27.06.09 20:47
Оценка:
Здравствуйте, rs4i, Вы писали:

R>Здравствуйте, Murom, Вы писали:


M>>Здравствуйте, rs4i, Вы писали:


R>>>Вопрос: как обеспечить уникальность типа?

R>>>Хочется чтоб выглядело приблизительно так:
R>>>
R>>>UniqueType< std::string > password = "******"; // уникальный тип
R>>>UniqueType< std::string > login = "rs4i"; // уникальный тип
R>>>password = login; // error
R>>>


M>>У нас в coding-style, например, запрещено определять несколько переменных на одной строке.

M>>Так нам вообще без присваиваний писать?

R>Не понял. Кто это мы? Где у меня в коде определения нескольких переменных на одной строке?


Ты не понял в чем вопрос. Если ты хочешь завести две совместимые переменные ОДНОГО типа UniqueType<std::string>, например, oldpassword и newpassword, то как ты это сделаешь в твоем примере?

А вот зайца кому, зайца-выбегайца?!
Re[3]: Реализация идеомы UniqueType
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 27.06.09 23:53
Оценка: 8 (1)
Здравствуйте, 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.: Винодельческие провинции — это есть рулез!
Re[3]: Реализация идеомы UniqueType
От: Юрий Жмеренецкий ICQ 380412032
Дата: 28.06.09 07:56
Оценка:
Здравствуйте, 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> {};

Не дописал пару строк. Этим предполагается инстанциировать
template<class Tag>class UniqueType {
 typename Tag::type v;
 //...
}
//...
UniqueType<name> n = ...;
Re[4]: offtop
От: Alexey F  
Дата: 28.06.09 09:32
Оценка:
Здравствуйте, Геннадий Васильев, Вы писали:

ГВ>
[...skip...]
ГВ>UniqueType<std::string, class Password_> pwd("secret password"); // Объявление типа и инициализация
ГВ>


А выделенное — легально с точки зрения стандарта?
Кажется, инстанцирование неполными типами приводит к ub... Или тут какое-то хитрое исключение?
Re[5]: offtop
От: Erop Россия  
Дата: 28.06.09 10:32
Оценка: 8 (1)
Здравствуйте, Alexey F, Вы писали:

AF>Кажется, инстанцирование неполными типами приводит к ub... Или тут какое-то хитрое исключение?

А разве это не для типов из STL так?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: offtop
От: Alexey F  
Дата: 28.06.09 12:07
Оценка: +2
Здравствуйте, 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"
Re[7]: offtop
От: Юрий Жмеренецкий ICQ 380412032
Дата: 28.06.09 13:14
Оценка: 21 (2)
Здравствуйте, 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"); // ok

void f()
{
  UniqueType<std::string, class Password_2> pwd("secret password"); //error
}
Re[8]: offtop
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 28.06.09 14:03
Оценка:
Здравствуйте, Юрий Жмеренецкий, Вы писали:

ЮЖ>Здесь еще другой аспект есть:

ЮЖ>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.: Винодельческие провинции — это есть рулез!
Re[9]: offtop
От: Юрий Жмеренецкий ICQ 380412032
Дата: 28.06.09 14:33
Оценка: 10 (1)
Здравствуйте, Геннадий Васильев, Вы писали:
...
ГВ>Да, согласен, именно такая конструкция допустима во фривольном 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;
}

никого не смущает =)
Re[5]: offtop
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 28.06.09 14:43
Оценка: 8 (1)
Здравствуйте, 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.: Винодельческие провинции — это есть рулез!
Re[10]: offtop
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 28.06.09 14:56
Оценка:
Здравствуйте, Юрий Жмеренецкий, Вы писали:

ГВ>>Да, согласен, именно такая конструкция допустима во фривольном VC++ (он локальными типами инстанцировать позволяет). Comeau ругается.

ЮЖ>VC++ с '/Za' должен(под рукой нет) ругаться.

Да, правильно, только использование хидеров windows плотно ассоциируется с отключённой /Za. А так, конечно.

ЮЖ>Defect report #488 отменяет ограничение на инстанцирование локальными/безымянными типами, поэтому согласно будущему стандарту такое использование вполне законно. Пример оттуда:


[skip]

Ах вот оно, в чём дело. Надо внимательней посмотреть на C++0x.

Но, кстати, по-моему, как раз тот случай, когда MS-овское "нарушение конвенций" вполне оправдано. В прочем, это уже офтоп.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[4]: Реализация идеомы UniqueType
От: Анатолий Широков СССР  
Дата: 28.06.09 15:57
Оценка: +1
Здравствуйте, Геннадий Васильев, Вы писали:

ГВ>Здравствуйте, 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("***");
Re[5]: Реализация идеомы UniqueType
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 28.06.09 16:37
Оценка:
Здравствуйте, Анатолий Широков, Вы писали:

АШ>Но с практической точки зрения тег можно нагрузить еще и полезной метаинформацией, тогда запись типа станет лаконичной, а код более читаемый:


[skip]

Не буду спорить: такой подход может оказаться более предпочтительным в каких-то ситуациях. Например, в тег можно поместить упомянутые уже здесь проверки ограничений и т.д. Я, правда, больше сторонник комбинирования свободных функций и очень мелких классов, которые можно привязать к идентификаторам типов. ИМХО, так гибче получается. Но, повторюсь, тут скорее дело вкуса.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.