Есть самопальная структура Variant (платформа — Linux):
struct Variant
{
public:
enum VariantType
{
VARIANT_BOOL,
VARIANT_DOUBLE,
VARIANT_PTR,
VARIANT_INTEGER,
VARIANT_DECIMAL,
VARIANT_LONG,
NUM_OF_VARIANT_TYPES,
};
private:
VariantType _type;
union
{
bool _bool_val;
double _dbl_val;
void * _ptr_val;
int _int_val;
long _long_val;
} _data;
public:
explicit Variant( bool val ) :
_type( VARIANT_BOOL )
{
_data._bool_val = val;
}
explicit Variant( double val ) :
_type( VARIANT_DOUBLE )
{
_data._dbl_val = val;
}
explicit Variant( void * val ) :
_type( VARIANT_PTR )
{
_data._ptr_val = val;
}
explicit Variant( int val ) :
_type( VARIANT_INTEGER )
{
_data._int_val = val;
}
explicit Variant( long val ) :
_type( VARIANT_LONG )
{
_data._long_val = val;
}
};
Вопрос в том, как реализовать арифметику для этого типа? Понятно, что, например, long c double, например, сравнивать можно, а long со строкой — нет смысла. Не писать же выбор допустимых типов перебором. Первое, что приходит в голову — создать квадратную матрицу с указателями на функции сравнения. Нормальное ли это решение, или я не туда копаю?
Здравствуйте, Аноним, Вы писали:
А>Есть самопальная структура Variant (платформа — Linux):
Закопай это.
А>Вопрос в том, как реализовать арифметику для этого типа? Понятно, что, например, long c double, например, сравнивать можно, а long со строкой — нет смысла. Не писать же выбор допустимых типов перебором. Первое, что приходит в голову — создать квадратную матрицу с указателями на функции сравнения. Нормальное ли это решение, или я не туда копаю?
Можно и матрицей, для такой убогой реализации.
В реализация посложнее — у тебя к типам, которые могут размещаться в варианте формируется статический объект класса самопального RAII, в котором прописываются возможные операторы.
class BicycleRAII
{
std::hash_map<const BicycleRAII*, Functor> m_LessOps;
template<class T>
const BicycleRAII* typeid()
{
static BicycleRAII desc( *(T*)0 );
return &desc;
}
};
class Variant // очень и очень приблизительно
{
const BicycleRAII* type;
void* value;
};
И где-то в ходе инициализации программы прописываешь то что нужно.
Здравствуйте, Аноним, Вы писали:
А>Вопрос в том, как реализовать арифметику для этого типа? Понятно, что, например, long c double, например, сравнивать можно, а long со строкой — нет смысла. Не писать же выбор допустимых типов перебором. Первое, что приходит в голову — создать квадратную матрицу с указателями на функции сравнения. Нормальное ли это решение, или я не туда копаю?
Квадратная матрица — самое простое решение. Единственная трудность будет в расширении набора типов. Но если типы заморожены, то почему бы и нет?
Только я бы предложил не матрицу функций, а матрицу классов-вычислителей.
struct IEvaluator
{
static Variant issue() { throw bad_variants_combination(); }
virtual Variant plus (Variant const& a, Variant const& b) const { return issue(); }
virtual Variant minus(Variant const& a, Variant const& b) const { return issue(); }
.....
};
template<class A, class B> // A и B - числовые типы
struct NumericEvaluator : IEvaluator
{
Variant plus (Variant const& a, Variant const& b) const { return Variant(a.get<A>() + a.get<B>(b)); }
Variant minus(Variant const& a, Variant const& b) const { return Variant(a.get<A>() + a.get<B>(b)); }
};
template<class A, class B>
struct MixedEvaluator : IEvaluator
{
.....
};
struct StringEvaluator : IEvaluator
{
.....
Variant minus(Variant const& a, Variant const& b) const
.....
};
template<class B> // ptr+idx, ptr-idx, ptr==0, возможно, ptr==intptr_t
struct PtrIntEvaluator : IEvaluator
{
.....
};
template<class A> // idx+ptr, idx-ptr, 0==ptr
struct IntPtrEvaluator : IEvaluator
{
.....
};
struct PtrEvaluator : IEvaluator // ptr-ptr, ptr==ptr, ptr!=ptr
{
.....
};
template<class E> E const* evaluator_instance() { E e; return &e; }
IEvaluator const* evaluators[NUM_OF_VARIANT_TYPES][NUM_OF_VARIANT_TYPES] =
{
// bool
{
evaluator_instance< NumericEvaluator<bool,bool> >,
evaluator_instance< NumericEvaluator<bool,double> >, // в арифметике bool продвигается до int, а далее всё понятно
.....
},
// double
{
.....
},
.....
};
Если очень хочется, то можно определить универсальный класс, который использует встроенные операторы... если они определены.
template<class A, class B>
struct CommonEvaluator : IEvaluator
{
Variant plus (Variant const& a, Variant const& b) const { return Variant(eval_plus(a.get<A>(), a.get<B>(b), 0)); }
auto eval_plus(A a, B b, decltype(a+b)* _) -> decltype(a+b) { return a+b; } // в расчёте на SFINAE
int eval_plus(...) { return int_issue(); }
.....
};
Только должен предупредить, что на сочетании void*, int у gcc4.7.3 срывает крышу. Кстати, сейчас минимальный код напишу.
<>
Универсальный — пишется немножко не так
namespace operators
{
// вот в этом случае SFINAE будет работать
template<class A, class B> auto plus(A a, B b) -> decltype(a+b) { return a+b; }
int plus(...) { throw some_error(); return 0; }
// и так для всех операторов
};
template<class A, class B>
struct Evaluator : IEvaluator
{
Variant plus(Variant a, Variant b) { return operators::plus(a.get<A>(), b.get<B>()); }
};
ну и сделать в operators ловушки-специализации для арифметики с void*, чтобы гусь не падал.
template<class A> int plus(A a, void* b) { throw some_error(); }
template<class B> int plus(void* a, B b) { throw some_error(); }