Сравнение переменных типа variant
От: Аноним  
Дата: 04.08.13 13:06
Оценка:
Есть самопальная структура 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 со строкой — нет смысла. Не писать же выбор допустимых типов перебором. Первое, что приходит в голову — создать квадратную матрицу с указателями на функции сравнения. Нормальное ли это решение, или я не туда копаю?
Re: Сравнение переменных типа variant
От: Nikе Россия  
Дата: 04.08.13 13:38
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Есть самопальная структура 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;
};


И где-то в ходе инициализации программы прописываешь то что нужно.
Нужно разобрать угил.
Re: Сравнение переменных типа variant
От: Кодт Россия  
Дата: 04.08.13 18:23
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Вопрос в том, как реализовать арифметику для этого типа? Понятно, что, например, 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 срывает крышу. Кстати, сейчас минимальный код напишу.
Перекуём баги на фичи!
Re[2]: Сравнение переменных типа variant
От: Кодт Россия  
Дата: 04.08.13 19:04
Оценка:
<>

Универсальный — пишется немножко не так
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(); }
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.