Перечитывая тему про signed vs unsigned...
От: Erop Россия  
Дата: 29.04.08 17:00
Оценка: 90 (3)
Поосмотрел я тут на всплывшие "минусы STL" и вспомнил ещё более древний флейм про signed vs unsigned.

И подумалось мне, что мастера выразительности и декларативности, скучающие по паскалевским отрезкам целых типов должны бы использовать что-то вроде следующего:
#ifndef IntRange_h
#define IntRange_h

#include <assert.h>

template<int TMinVal, int TMaxVal>
struct IntRange {
    enum Type {
        MinValue = TMinVal, 
        MaxValue = TMaxVal
    };

    static bool IsValid( int a ) { return MinValue <= a && a <= MaxValue; }
    static Type To( int a ) { assert( IsValid( a ) ); return static_cast<Type>( a ); }
    static Type& SetTo( Type& dst, int a2 ) { return dst = To( a2 ); }
    static Type Plus( int a1, int a2 ) { return To( a1 + a2 ); }
    static Type Minus( int a1, int a2 ) { return To( a1 - a2 ); }
    static Type Mult( int a1, int a2 ) { return To( a1 * a2 ); }
    static Type Div( int a1, int a2 ) { assert( a2 != 0 ); return To( a1 / a2 ); }
    static Type Rest( int a1, int a2 ) { assert( a2 != 0 ); return To( a1 % a2 ); }

    friend inline bool IsValid( Type a ) { return IntRange::IsValid( a ); }
    friend inline Type MinValue( const Type& ) { return IntRange::MinValue; }
    friend inline Type MaxValue( const Type& ) { return IntRange::MaxValue; }
    friend inline Type& SetTo( Type& dst, int a2 ) { return IntRange::SetTo( dst, a2 ); }
    friend inline bool IsMinValue( Type a ) { return a == MinValue; }
    friend inline bool IsMaxValue( Type a ) { return a == MaxValue; }
    friend inline Type Succ( Type a ) { return a + 1; }
    friend inline Type Pred( Type a ) { return a - 1; }

    friend inline Type operator + ( Type a1, Type a2 )    { return Plus( a1, a2 ); }
    friend inline Type operator + ( Type a1, int a2 )    { return Plus( a1, a2 ); }
    friend inline Type operator + ( int a1, Type a2 )    { return Plus( a1, a2 ); }
    friend inline Type& operator += ( Type& dst, int a2 ) { return dst = dst + a2; }
    
    friend inline Type operator - ( Type a1, Type a2 )    { return Minus( a1, a2 ); }
    friend inline Type operator - ( Type a1, int a2 )    { return Minus( a1, a2 ); }
    friend inline Type operator - ( int a1, Type a2 )    { return Minus( a1, a2 ); }
    friend inline Type& operator -= ( Type& dst, int a2 ) { return dst = dst - a2; }

    friend inline Type operator * ( Type a1, Type a2 )    { return Mult( a1, a2 ); }
    friend inline Type operator * ( Type a1, int a2 )    { return Mult( a1, a2 ); }
    friend inline Type operator * ( int a1, Type a2 )    { return Mult( a1, a2 ); }
    friend inline Type& operator *= ( Type& dst, int a2 ) { return dst = dst * a2; }

    friend inline Type operator / ( Type a1, Type a2 )    { return Div( a1, a2 ); }
    friend inline Type operator / ( Type a1, int a2 )    { return Div( a1, a2 ); }
    friend inline Type operator / ( int a1, Type a2 )    { return Div( a1, a2 ); }
    friend inline Type& operator /= ( Type& dst, int a2 ) { return dst = dst / a2; }

    friend inline Type operator % ( Type a1, Type a2 )    { return Rest( a1, a2 ); }
    friend inline Type operator % ( Type a1, int a2 )    { return Rest( a1, a2 ); }
    friend inline Type operator % ( int a1, Type a2 )    { return Rest( a1, a2 ); }
    friend inline Type& operator %= ( Type& dst, int a2 ) { return dst = dst % a2; }

    friend inline Type operator ++ ( Type& a )    { return a += 1; }
    friend inline Type operator ++ ( Type& a, int )    { Type res = a; a += 1; return res; }
    friend inline Type operator -- ( Type& a )    { return a -= 1; }
    friend inline Type operator -- ( Type& a, int )    { Type res = a; a -= 1; return res; }

};

#endif//!IntRange_h


Ну а используется оно как-то так:
    IntRange<1, 3>::Type x = MinValue( x );
    x = x + 1;
    x += 1;
    --x;
    x++;    IntRange<-2, 2>::Type y = MaxValue( y );
    Set( x, y );
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Отредактировано 11.05.2020 7:25 Erop . Предыдущая версия .
Re: Перечитывая тема про signed vs unsigned...
От: Roman Odaisky Украина  
Дата: 29.04.08 17:06
Оценка: :)
Здравствуйте, Erop, Вы писали:

E>Поосмотрел я тут на всплывшие "минусы STL" и вспомнил ещё более древний флейм про signed vs unsigned.


E>И подумалось мне, что мастера выразительности и декларативности, скучающие по паскалевским отрезкам целых типов должны бы использовать что-то вроде следующего:


Для этого есть boost::numeric_cast, если сильно хочется. И вообще, если некий тип может содержать числа только от 0 до 42 и при этом не является enum, то что-то крепко не так.

Я сейчас думаю, что unsigned — это как коммунизм: правильное решение, но общество не доросло.
До последнего не верил в пирамиду Лебедева.
Re[2]: Перечитывая тема про signed vs unsigned...
От: Erop Россия  
Дата: 29.04.08 17:25
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

RO>Для этого есть boost::numeric_cast, если сильно хочется. И вообще, если некий тип может содержать числа только от 0 до 42 и при этом не является enum, то что-то крепко не так.

Посмотри внимательнее! IntRange<0, 42>::Type -- он enum и есть!!!

А чем тут поможет boost::numeric_cast?

RO>Я сейчас думаю, что unsigned — это как коммунизм: правильное решение, но общество не доросло.


Эта тема про другое...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Перечитывая тема про signed vs unsigned...
От: Roman Odaisky Украина  
Дата: 29.04.08 17:30
Оценка:
Здравствуйте, Erop, Вы писали:

RO>>Для этого есть boost::numeric_cast, если сильно хочется. И вообще, если некий тип может содержать числа только от 0 до 42 и при этом не является enum, то что-то крепко не так.

E>Посмотри внимательнее! IntRange<0, 42>::Type -- он enum и есть!!! :)

enum Colors
{
    AliceBlue = 0,
    AntiqueWhite = 1,
    BlueViolet = 2,
    . . .
    White = 41,
    Yellow = 42,
};


E>А чем тут поможет boost::numeric_cast?


Он как раз использует std::numeric_limits<X>::{min|max}.
До последнего не верил в пирамиду Лебедева.
Re[4]: Перечитывая тема про signed vs unsigned...
От: Erop Россия  
Дата: 29.04.08 17:45
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:


RO>
RO>enum Colors
RO>{
RO>    AliceBlue = 0,
RO>    AntiqueWhite = 1,
RO>    BlueViolet = 2,
RO>    . . .
RO>    White = 41,
RO>    Yellow = 42,
RO>};
RO>


E>>А чем тут поможет boost::numeric_cast?


RO>Он как раз использует std::numeric_limits<X>::{min|max}.


Погоди, как он поможет родить тип, являющийся динамически контролируемым отрезком int?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Перечитывая тема про signed vs unsigned...
От: _nn_ www.nemerleweb.com
Дата: 29.04.08 17:45
Оценка:
Здравствуйте, Erop, Вы писали:

E>Поосмотрел я тут на всплывшие "минусы STL" и вспомнил ещё более древний флейм про signed vs unsigned.


E>И подумалось мне, что мастера выразительности и декларативности, скучающие по паскалевским отрезкам целых типов должны бы использовать что-то вроде следующего:


На правах саморекламы
auto_value 2
Автор: _nn_
Дата: 07.01.05
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[5]: Перечитывая тема про signed vs unsigned...
От: Roman Odaisky Украина  
Дата: 29.04.08 17:55
Оценка:
Здравствуйте, Erop, Вы писали:

E>>>А чем тут поможет boost::numeric_cast?

RO>>Он как раз использует std::numeric_limits<X>::{min|max}.
E>Погоди, как он поможет родить тип, являющийся динамически контролируемым отрезком int?

Можно сделать тип, специализировать им std::numeric_limits, и на каждый чих вызывать boost::numeric_cast.
До последнего не верил в пирамиду Лебедева.
Re[6]: Перечитывая тема про signed vs unsigned...
От: Erop Россия  
Дата: 29.04.08 18:04
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

RO>Можно сделать тип, специализировать им std::numeric_limits, и на каждый чих вызывать boost::numeric_cast.

Не, это не кузёво.
Всё-таки IntRange<5, 18> будет работать сам. Автоматически...

Кстати, а что ты ожидаешь от выражения color + 8?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Перечитывая тема про signed vs unsigned...
От: Erop Россия  
Дата: 29.04.08 18:11
Оценка:
Здравствуйте, _nn_, Вы писали:

__>На правах саморекламы

__>auto_value 2
Автор: _nn_
Дата: 07.01.05


Это не прикольно, вернее это не то.
IntRange<8, 12>::Type -- это настоящий enum, то есть стандартный тип. Его, например, все компиляторы будут передавать через регистры, будут всякие fastcall работать, отладчики показывать и всё такое
Его преобразование в int будет не пользовательским, его можно хранить в bitfields и т. д.
Скажем можно написать так:
IntRange<5, 12>::type myVecSize;
Set( myVecSize, getVecSize() );
std::vector<int> v( myVecSize );


Всё честно. Никаких указателей, никаких объектов, чистый честный c++ enum...

Если таки требование выражать требования к диапазону допустимых значений так насущно необходимо, как участники той дискуссии утвеждали (а я не думаю, что они писали не то, что думают), то, IMHO, очень в тему конструкиця, таки...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Перечитывая тема про signed vs unsigned...
От: Erop Россия  
Дата: 29.04.08 18:50
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

RO>>>И вообще, если некий тип может содержать числа только от 0 до 42 и при этом не является enum, то что-то крепко не так.

E>>Посмотри внимательнее! IntRange<0, 42>::Type -- он enum и есть!!!

RO>
RO>enum Colors
RO>{
RO>    AliceBlue = 0,
RO>    AntiqueWhite = 1,
RO>    BlueViolet = 2,
RO>    . . .
RO>    White = 41,
RO>    Yellow = 42,
RO>};
RO>


Во-первых, таки это обозначает, что ты считаешь, что отрезки целого ряда не нужны.
Хотя вполне может быть что-то типа числа месяца, которое должно быть от 1 до 31...
Или, скажем, такой тип, как "число процентов", которое от 0 до 100...

Во-вторых, если ты считаешь, что нормально, что White + 1 == Yellow, то можно сделать как-то так:
template<typename TEnum, TEnum  TMinVal, TEnum TMaxVal>
struct EnumWithMathProcessor {
    typedef TEnum Type;
    static const Type MinValue = TMinVal;
    static const Type MaxValue = TMaxVal;
    
    static bool IsValid( int a ) { return MinValue <= a && a <= MaxValue; }
    static Type To( int a ) { assert( IsValid( a ) ); return static_cast<Type>( a ); }
    static Type& SetTo( Type& dst, int a2 ) { return dst = To( a2 ); }
    static Type Plus( int a1, int a2 ) { return To( a1 + a2 ); }
    static Type Minus( int a1, int a2 ) { return To( a1 - a2 ); }
    static Type Mult( int a1, int a2 ) { return To( a1 * a2 ); }
    static Type Div( int a1, int a2 ) { assert( a2 != 0 ); return To( a1 / a2 ); }
    static Type Rest( int a1, int a2 ) { assert( a2 != 0 ); return To( a1 % a2 ); }
};

#define ENUM_WITH_MATH_FUNCTIONS( TProcessor )    \
    friend inline TProcessor& GetProcessor( const TProcessor::Type& ) { static TProcessor p; return p; } \
    friend inline bool IsValid( TProcessor::Type a ) { return TProcessor::IsValid( a ); } \
    friend inline TProcessor::Type MinValue( const TProcessor::Type& ) { return TProcessor::MinValue; } \
    friend inline TProcessor::Type MaxValue( const TProcessor::Type& ) { return TProcessor::MaxValue; } \
    friend inline TProcessor::Type& SetTo( TProcessor::Type& dst, int a2 ) { return TProcessor::SetTo( dst, a2 ); } \
    friend inline bool IsMinValue( TProcessor::Type a ) { return a == TProcessor::MinValue; } \
    friend inline bool IsMaxValue( TProcessor::Type a ) { return a == TProcessor::MaxValue; } \
    friend inline TProcessor::Type Succ( TProcessor::Type a ) { return TProcessor::Plus( a, 1 ); } \
    friend inline TProcessor::Type Pred( Type a ) { return TProcessor::Minus( a, 1 ); } \

#define ENUM_WITH_MATH_OPERATORS( TProcessor ) \
    friend inline TProcessor::Type operator + ( TProcessor::Type a1, TProcessor::Type a2 )    { return TProcessor::Plus( a1, a2 ); } \
    friend inline TProcessor::Type operator + ( TProcessor::Type a1, int a2 )    { return TProcessor::Plus( a1, a2 ); } \
    friend inline TProcessor::Type operator + ( int a1, TProcessor::Type a2 )    { return TProcessor::Plus( a1, a2 ); } \
    friend inline TProcessor::Type& operator += ( TProcessor::Type& dst, int a2 ) { return dst = dst + a2; } \
     \
    friend inline TProcessor::Type operator - ( TProcessor::Type a1, TProcessor::Type a2 )    { return TProcessor::Minus( a1, a2 ); } \
    friend inline TProcessor::Type operator - ( TProcessor::Type a1, int a2 )    { return TProcessor::Minus( a1, a2 ); } \
    friend inline TProcessor::Type operator - ( int a1, TProcessor::Type a2 )    { return TProcessor::Minus( a1, a2 ); } \
    friend inline TProcessor::Type& operator -= ( TProcessor::Type& dst, int a2 ) { return dst = dst - a2; } \
     \
    friend inline TProcessor::Type operator * ( TProcessor::Type a1, TProcessor::Type a2 )    { return TProcessor::Mult( a1, a2 ); } \
    friend inline TProcessor::Type operator * ( TProcessor::Type a1, int a2 )    { return TProcessor::Mult( a1, a2 ); } \
    friend inline TProcessor::Type operator * ( int a1, TProcessor::Type a2 )    { return TProcessor::Mult( a1, a2 ); } \
    friend inline TProcessor::Type& operator *= ( TProcessor::Type& dst, int a2 ) { return dst = dst * a2; } \
     \
    friend inline TProcessor::Type operator / ( TProcessor::Type a1, TProcessor::Type a2 )    { return TProcessor::Div( a1, a2 ); } \
    friend inline TProcessor::Type operator / ( TProcessor::Type a1, int a2 )    { return TProcessor::Div( a1, a2 ); } \
    friend inline TProcessor::Type operator / ( int a1, TProcessor::Type a2 )    { return TProcessor::Div( a1, a2 ); } \
    friend inline TProcessor::Type& operator /= ( TProcessor::Type& dst, int a2 ) { return dst = dst / a2; } \
     \
    friend inline TProcessor::Type operator % ( TProcessor::Type a1, TProcessor::Type a2 )    { return TProcessor::Rest( a1, a2 ); } \
    friend inline TProcessor::Type operator % ( TProcessor::Type a1, int a2 )    { return TProcessor::Rest( a1, a2 ); } \
    friend inline TProcessor::Type operator % ( int a1, TProcessor::Type a2 )    { return TProcessor::Rest( a1, a2 ); } \
    friend inline TProcessor::Type& operator %= ( TProcessor::Type& dst, int a2 ) { return dst = dst % a2; } \
     \
    friend inline TProcessor::Type operator ++ ( TProcessor::Type& a )    { return a += 1; } \
    friend inline TProcessor::Type operator ++ ( TProcessor::Type& a, int )    { TProcessor::Type res = a; a += 1; return res; } \
    friend inline TProcessor::Type operator -- ( TProcessor::Type& a )    { return a -= 1; } \
    friend inline TProcessor::Type operator -- ( TProcessor::Type& a, int )    { TProcessor::Type res = a; a -= 1; return res; } \

#define BEGIN_ENUM_WITH_MATH( enumName ) \
struct enumName {\
    enum Type {

#define END_ENUM_WITH_MATH( minVal, maxVal )\
    }; \
    typedef EnumWithMathProcessor<Type, minVal, maxVal> Processor; \
    ENUM_WITH_MATH_FUNCTIONS( Processor ) \
    ENUM_WITH_MATH_OPERATORS( Processor ) \
};


Ну и пользоваться как-то так:
BEGIN_ENUM_WITH_MATH( RGB )
   Red, Green, Blue
END_ENUM_WITH_MATH( Red, Blue )

void iterateColours()
{
   const RGB::Type prohColor = RGD::Green; // Объект скорее всего не заведётся, так как RGB::Type -- честный enum!!!
   for( RGB::Type color = MinValue( color ); ; color++ ) {
       if( color != prohColor )
           processColor( color );
       if( IsMaxValue( color ) )
           break;
   }
}
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Перечитывая тема про signed vs unsigned...
От: igna Россия  
Дата: 30.04.08 06:09
Оценка: :)
Можно перенести проверку и на этап компиляции. Вот набросок:

template <int MIN, int MAX>
class ranged_int {
    int i_;
public:
    ranged_int() {}
    ranged_int(int i) : i_(i) {}
    int value() { return i_; }
};

template <int MIN1, int MAX1, int MIN2, int MAX2>
ranged_int<MIN1 + MIN2, MAX1 + MAX2> operator+(
    ranged_int<MIN1, MAX1> x, ranged_int<MIN2, MAX2> y)
{
    return ranged_int<MIN1 + MIN2, MAX1 + MAX2>(x.value() + y.value());
}

int main()
{
    ranged_int<0, 10> a(3), b(8);
    ranged_int<0, 20> c;
    c = a + b;
    ranged_int<0, 10> d;
    d = a + b; // error: no operator "=" matches these operands
                // operand types are: ranged_int<0, 10> = ranged_int<0, 20>
}
Re[2]: Перечитывая тема про signed vs unsigned...
От: Erop Россия  
Дата: 30.04.08 16:32
Оценка:
Здравствуйте, igna, Вы писали:

I>Можно перенести проверку и на этап компиляции. Вот набросок:

I>    d = a + b; // error: no operator "=" matches these operands
I>                // operand types are: ranged_int<0, 10> = ranged_int<0, 20>
I>}
I>


А в чём идея проверять на этапе компиляции rt-условия?
проще уж тогда просто константное выражение посчитать...

Кроме того, твой ranged_int -- это объект класса, а IntRange<1, 8>::Type -- это честный enum, стандартный тип, то, сё...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.