I'm writing a class template that can take one of the following, int8_t, int16_t, int32_t, float and double. Here it is:
#ifndef OPERAND_HPP
# define OPERAND_HPP
# include "IOperand.hpp"
# include <regex>
template <typename T>
class Operand : public IOperand {
private:
/* Constructors - Destructors */
Operand() = default;
/* Attributes */
T _num;
eOperandType const _type;
std::string const &_value;
/* Static functions */
static int8_t getValue(Operand<int8_t> const *it) { return it->getNum(); }
static int16_t getValue(Operand<int16_t> const *it) { return it->getNum(); }
static int32_t getValue(Operand<int32_t> const *it) { return it->getNum(); }
static float getValue(Operand<float> const *it) { return it->getNum(); }
static double getValue(Operand<double> const *it) { return it->getNum(); }
public:
/* Constructors - Destructors */
explicit Operand(eOperandType t, std::string const &v, T n) : _num(n), _type(t), _value(v) {}
Operand(Operand const &rhs) : Operand(rhs.getType(), rhs.toString(), rhs.getNum()) {};
~Operand() override = default;
/* Operator overloads */
Operand<T> &operator=(Operand const &rhs) {
_num = rhs.getNum();
_type = rhs.getType();
_value = rhs.toString();
return *this;
}
IOperand const *operator+(IOperand const &rhs) const final {
Operand::getValue(&rhs);
return &rhs;
}
IOperand const *operator-(IOperand const &rhs) const final { return &rhs; }
IOperand const *operator*(IOperand const &rhs) const final { return &rhs; }
IOperand const *operator/(IOperand const &rhs) const final { return &rhs; }
IOperand const *operator%(IOperand const &rhs) const final { return &rhs; }
/* ------------------------------------------------- Exceptions ------------------------------------------------- */
class DivisionByZero : public std::exception {
public:
char const *what() const noexcept final { return "division by zero"; }
};
class ModuloByZero : public std::exception {
public:
char const *what() const noexcept final { return "modulo by zero"; }
};
class Overflow : public std::exception {
public:
char const *what() const noexcept final { return "overflow"; }
};
class Underflow : public std::exception {
public:
char const *what() const noexcept final { return "underflow"; }
};
/* -------------------------------------------------------------------------------------------------------------- */
/* Member functions */
T getNum() const { return _num; }
int getPrecision() const final { return static_cast<int>(_type); }
eOperandType getType() const final { return _type; }
std::string const &toString() const final { return _value; }
};
#endif /* OPERAND_HPP */
That class inherit from the following interface:
#ifndef IOPERAND_HPP
# define IOPERAND_HPP
# include <string>
enum eOperandType {
INT8,
INT16,
INT32,
FLOAT,
DOUBLE
};
class IOperand {
public:
/* Constructors - Destructors */
virtual ~IOperand() = default;
/* Operator overloads */
virtual IOperand const *operator+(IOperand const &) const = 0;
virtual IOperand const *operator-(IOperand const &) const = 0;
virtual IOperand const *operator*(IOperand const &) const = 0;
virtual IOperand const *operator/(IOperand const &) const = 0;
virtual IOperand const *operator%(IOperand const &) const = 0;
/* Member functions */
virtual int getPrecision() const = 0;
virtual eOperandType getType() const = 0;
virtual std::string const &toString() const = 0;
};
#endif /* IOPERAND_HPP */
I'm struggling to implement the + operator overload. That function has two IOperand & as arguments, but I need to access the _num attribute of said arguments. The getter for this attribute isn't in the base class, only in the derived class. I tried polymorphism with the following functions:
static int8_t getValue(Operand<int8_t> const *it) { return it->getNum(); }
static int16_t getValue(Operand<int16_t> const *it) { return it->getNum(); }
static int32_t getValue(Operand<int32_t> const *it) { return it->getNum(); }
static float getValue(Operand<float> const *it) { return it->getNum(); }
static double getValue(Operand<double> const *it) { return it->getNum(); }
but I get the following error message when compiling with clang++:
./Operand.tpp:41:15: error: no matching function for call to 'getValue'
(void)Operand::getValue(&rhs);
./Operand.tpp:19:29: note: candidate function not viable: cannot convert from base class pointer 'const IOperand *' to derived class pointer 'const Operand<int8_t> *'
(aka 'const Operand<signed char> *') for 1st argument
static int8_t getValue(Operand<int8_t> const *it) { return it->getNum(); }
The error message is pretty verbose and I understand where the problem lies. However I'm not sure how to fix it, or if there would be a more elegant approach to my problem. I could probably get away with it using dynamic casts, or even using 5 different classes instead of a template, but it doesn't seem that good.
Could anyone point me in the right direction?
Related
after some searching on the Net, I found a satisfying, at least for me, solution for C++ 14 enums inheritance, as you can see in the code below, where only base class is mentioned. Everything works fine except for switch/case usage where a compilation error occurs.
May someone suggest a simple modification to MyEnum class in order to be used in switch/case statements, too?
Many thanks.
#include <iostream>
#include <cstdint>
class MyEnum
{
public:
static const MyEnum VAL_1;
static const MyEnum VAL_2;
static const MyEnum VAL_3;
MyEnum() {}
explicit MyEnum(uint8_t val) : value(val) {}
virtual ~MyEnum() {}
bool operator<(const MyEnum& other) const { return value < other.value; }
bool operator==(const MyEnum& other) const { return value == other.value; }
bool operator!=(const MyEnum& other) const { return !(operator==(other)); }
operator uint8_t() const { return value; }
protected:
static const uint8_t Val1 = 1;
static const uint8_t Val2 = 2;
static const uint8_t Val3 = 3;
uint8_t value;
};
const MyEnum MyEnum::VAL_1(Val1);
const MyEnum MyEnum::VAL_2(Val2);
const MyEnum MyEnum::VAL_3(Val3);
int main()
{
MyEnum e;
e = MyEnum::VAL_1;
if (e == MyEnum::VAL_1)
std::cout << "Compiles" << std::endl;
switch (e)
{
case MyEnum::VAL_1:
{
std::cout << "Doesn't compile" << std::endl;
}
}
exit(0);
}
Thanks for all your answers, as I stated in my first post, my issue is mainly on C++ enums inheritance, then please find below an updated example.
Currently I have 2 issues:
The first MyEnum class implementation stops working enabling toString() methods, just compile with -DTO_STRING and you can see the effect on e value. I understand i'm trying to do something ugly, is there a correct way to achieve the same result?
I have a bunch of code using functions that take MyEnum type as input and I cannot modify it. Using the second suggested MyEnum implementation that wraps real enum, just compile with -DMYENUM_2, I get an error because wrapped enum type (vale) is expected as function input, not MyEnum itself. Any ideas?
Thanks again.
#include <iostream>
#include <cstdint>
#ifndef MYENUM_2
class MyEnum
{
public:
static const MyEnum VAL_1;
static const MyEnum VAL_2;
static const MyEnum VAL_3;
MyEnum() {}
constexpr explicit MyEnum(uint8_t val) : value(val) {}
bool operator<(const MyEnum& other) const { return value < other.value; }
bool operator==(const MyEnum& other) const { return value == other.value; }
bool operator!=(const MyEnum& other) const { return !(operator==(other)); }
constexpr operator uint8_t() const { return value; }
#ifdef TO_STRING
virtual std::string toString()
{
printf("value = %d\n", value);
if (value == Val1)
return "1";
else if (value == Val2)
return "2";
else if (value == Val3)
return "3";
}
#endif
protected:
static const uint8_t Val1 = 1;
static const uint8_t Val2 = 2;
static const uint8_t Val3 = 3;
uint8_t value;
};
constexpr MyEnum MyEnum::VAL_1(Val1);
constexpr MyEnum MyEnum::VAL_2(Val2);
constexpr MyEnum MyEnum::VAL_3(Val3);
class MyDerivedEnum : public MyEnum
{
public:
static const MyDerivedEnum VAL_4;
static const MyDerivedEnum VAL_5;
static const MyDerivedEnum VAL_6;
MyDerivedEnum() {}
constexpr explicit MyDerivedEnum(uint8_t val) : MyEnum(val) {}
#ifdef TO_STRING
std::string toString()
{
printf("value = %d\n", value);
if (value == Val4)
return "4";
else if (value == Val5)
return "5";
else if (value == Val6)
return "6";
}
#endif
private:
static const uint8_t Val4 = 4;
static const uint8_t Val5 = 5;
static const uint8_t Val6 = 6;
};
constexpr MyDerivedEnum MyDerivedEnum::VAL_4(Val4);
constexpr MyDerivedEnum MyDerivedEnum::VAL_5(Val5);
constexpr MyDerivedEnum MyDerivedEnum::VAL_6(Val6);
#else
class MyEnum
{
public:
typedef enum
{
VAL_1 = 1,
VAL_2,
VAL_3
} vale;
MyEnum() {}
explicit MyEnum(uint8_t val) : value((vale)val) {}
virtual ~MyEnum() {}
bool operator<(const MyEnum& other) const { return value < other.value; }
bool operator==(const MyEnum& other) const { return value == other.value; }
bool operator!=(const MyEnum& other) const { return !(operator==(other)); }
//replaced by operator vale :
//operator uint8_t() const { return value; }
MyEnum& operator = (const vale& v)
{
value = v;
return *this;
}
operator vale () const
{
return value;
}
//op ==, !=... to be written
protected:
vale value;
};
#endif
class A
{
public:
A() {}
~A() {}
void set(MyEnum e)
{
printf("e = %d\n", e);
}
};
#ifndef MYENUM_2
class B : A
{
public:
B() {}
~B() {}
void test(MyDerivedEnum e)
{
set(e);
}
};
#endif
int main()
{
A a;
#ifndef MYENUM_2
B b;
#endif
a.set(MyEnum::VAL_1);
#ifndef MYENUM_2
b.test(MyDerivedEnum::VAL_4);
#endif
exit(0);
}
Slight modifications to compiles:
class MyEnum
{
public:
static const MyEnum VAL_1;
static const MyEnum VAL_2;
static const MyEnum VAL_3;
MyEnum() {}
constexpr explicit MyEnum(uint8_t val) : value(val) {}
bool operator<(const MyEnum& other) const { return value < other.value; }
bool operator==(const MyEnum& other) const { return value == other.value; }
bool operator!=(const MyEnum& other) const { return !(operator==(other)); }
constexpr operator uint8_t() const { return value; }
protected:
static const uint8_t Val1 = 1;
static const uint8_t Val2 = 2;
static const uint8_t Val3 = 3;
uint8_t value;
};
constexpr MyEnum MyEnum::VAL_1(Val1);
constexpr MyEnum MyEnum::VAL_2(Val2);
constexpr MyEnum MyEnum::VAL_3(Val3);
Demo
What about using Pete Becker' solution ? Add an enumerated type -and associated conversion operators- ? You can do it this way
#include <iostream>
#include <cstdint>
class MyEnum
{
public:
typedef enum {
VAL_1 = 1,
VAL_2,
VAL_3
} vale;
MyEnum() {}
explicit MyEnum(uint8_t val) : value((vale)val) {}
virtual ~MyEnum() {}
bool operator<(const MyEnum& other) const { return value < other.value; }
bool operator==(const MyEnum& other) const { return value == other.value; }
bool operator!=(const MyEnum& other) const { return !(operator==(other)); }
//replaced by operator vale :
//operator uint8_t() const { return value; }
MyEnum& operator = (const vale& v) {
value = v;
return *this;
}
operator vale () const {
return value;
}
//op ==, !=... to be written
protected:
vale value;
};
int main() {
MyEnum e;
e = MyEnum::VAL_1;
if (e == MyEnum::VAL_1)
std::cout << "Compiles" << std::endl;
switch (e) {
case MyEnum::VAL_1:
std::cout << "compiles now" << std::endl;
break;
}
exit(0);
}
How do I get an overloaded relational operator in a class to be called from a function in the parent class that is handed a const reference to a base class as a parameter? The following code demonstrates what I would like to do:
class Object
{
public:
virtual ~Object(void);
virtual int compare(Object const& obj) const;
};
int Object::compare(Object const & obj) const {
if(this == &obj)
{
return 0;
}
else if(this < &obj)
{
return -1;
} else{
return 1;
}
}
class Integer: public Object
{
private:
int myInt;
public:
Integer(int i);
bool operator==(const Integer& integer);
};
bool Integer::operator==(Integer const &integer) {
if(myInt == integer.myInt)
{
return true;
}
return false;
}
How do I get the compare function in the base class to call the == operator in the child class, keeping in mind I have other child classes as well?
I have tried dynamic_cast<> but for some reason it wont work.
You can add another virtual method isEqual to connect with the Integer::operator==.
The only requirement is to keep the Object::isEqual signature in the class Integer. You should also keep in mind that your Object::compare method can be (accidentally) called on objects of different types.
class Object
{
protected:
virtual bool isEqual(Object const& obj) const
{ return this == &obj; }
public:
virtual ~Object(void);
virtual int compare(Object const& obj) const;
};
int Object::compare(Object const & obj) const {
if(isEqual(obj))
{
return 0;
}
else if(this < &obj)
{
return -1;
} else{
return 1;
}
}
class Integer: public Object
{
private:
int myInt;
protected:
virtual bool isEqual(Object const& obj) const
{ if (!dynamic_cast<const Integer*>(&obj))
return false;
return *this == (const Integer&) obj;
}
public:
Integer(int i);
bool operator==(const Integer& integer);
};
I would do:
#include <iostream>
class Object
{
public:
virtual ~Object(void) {};
int compare(Object const& obj) const;
virtual bool operator==(Object const& integer) const = 0;
virtual bool operator<(Object const& integer) const = 0;
virtual bool operator>(Object const& integer) const = 0;
};
int Object::compare(Object const& obj) const
{
if(*this == obj)
return 0;
else if(*this < obj)
return -1;
else return 1;
}
class Integer: public Object
{
private:
int myInt;
public:
Integer(int i) : myInt(i) { };
virtual bool operator==(Object const& integer) const override;
virtual bool operator<(Object const& integer) const override;
virtual bool operator>(Object const& integer) const override;
};
bool Integer::operator==(Object const& integer) const
{
return myInt == dynamic_cast<Integer const&>(integer).myInt;
}
bool Integer::operator<(Object const& integer) const
{
return myInt < dynamic_cast<Integer const&>(integer).myInt;
}
bool Integer::operator>(Object const& integer) const
{
return myInt > dynamic_cast<Integer const&>(integer).myInt;
}
int main()
{
Integer a(2), b(2), c(3);
std::cout << a.compare(b) << std::endl;
std::cout << b.compare(c) << std::endl;
std::cout << c.compare(a) << std::endl;
}
But in reality you should just provide virtual compare function in inherited class like so:
class Object
{
public:
virtual ~Object(void) {};
virtual int compare(Object const& obj) const = 0;
};
class Integer: public Object
{
private:
int myInt;
public:
Integer(int i) : myInt(i) { };
virtual int compare(Object const& object) const override;
bool operator==(Integer const& integer) const;
bool operator<(Integer const& integer) const;
bool operator>(Integer const& integer) const;
};
int Integer::compare(Object const& object) const
{
Integer const& ref = dynamic_cast<Integer const&>(object);
if(ref == *this)
return 0;
else if(ref > *this)
return 1;
else return -1;
}
bool Integer::operator==(Integer const& integer) const
{
return myInt == integer.myInt;
}
bool Integer::operator<(Integer const& integer) const
{
return myInt > integer.myInt;
}
bool Integer::operator>(Integer const& integer) const
{
return myInt < integer.myInt;
}
This question already has answers here:
In a templated derived class, why do I need to qualify base class member names with "this->" inside a member function?
(2 answers)
Closed 9 years ago.
I am writing template class for a sudoku puzzle, where the template parameters define the size of the rows and columns. I am using g++-4.8 with C++11 enabled.
I have one compiling issue that I worked around, but I would like to understand why it is not working as expected:
My RowIteratorImpl class derives from VirtualLineIteratorImpl, but I cannot access its fields virtualLineIdx and cellInVirtualLineIdx although this should be possible:
class VirtualLineIteratorImpl : public CellIteratorImpl
{
protected:
size_t virtualLineIdx;
size_t cellInVirtualLineIdx;
public:
VirtualLineIteratorImpl(size_t virtualLineIdx)
: virtualLineIdx(virtualLineIdx), cellInVirtualLineIdx(0)
{}
virtual void increment(size_t offset)
{
virtualLineIdx += offset;
}
};
class RowIteratorImpl : public VirtualLineIteratorImpl
{
public:
using VirtualLineIteratorImpl::VirtualLineIteratorImpl;
virtual size_t getCellIdx() const
{
// TODO: does not compile
// return mivTSudoku::getCellIdxInRow(virtualLineIdx, cellInVirtualLineIdx);
return mivTSudoku::getCellIdxInRow(VirtualLineIteratorImpl::virtualLineIdx, VirtualLineIteratorImpl::cellInVirtualLineIdx);
}
};
The compiler generates the following message:
mivSudoku.h: In member function ‘virtual size_t mivTSudoku::RowIteratorImpl::getCellIdx() const’:
mivSudoku.h:85:39: error: ‘virtualLineIdx’ was not declared in this scope
return mivTSudoku::getCellIdxInRow(virtualLineIdx, cellInVirtualLineIdx);
This is the entire class definition:
#ifndef MIVSUDOKU_H
#define MIVSUDOKU_H
#include <bitset>
#include <iterator>
#include <memory>
template<size_t n, size_t m=n>
class mivTSudoku
{
public:
class Cell
{};
static constexpr size_t getCellIdx(size_t rowIdx, size_t colIdx)
{
return rowIdx * dim + colIdx;
}
static constexpr size_t getCellIdxInRow(size_t rowIdx, size_t cellInRowIdx)
{
return getCellIdx(rowIdx, cellInRowIdx);
}
static constexpr size_t getCellIdxInColumn(size_t columnIdx, size_t cellInColumnIdx)
{
return getCellIdx(cellInColumnIdx, columnIdx);
}
static constexpr size_t getCellIdxInBlock(size_t blockIdx, size_t cellInBlockIdx)
{
return getCellIdx((blockIdx / n) * n + (cellInBlockIdx / m), (blockIdx % n) * m + (cellInBlockIdx % m));
}
class CellIteratorImpl
{
public:
virtual CellIteratorImpl* clone() = 0;
virtual void increment(size_t) = 0;
virtual void getCellIdx() const = 0;
};
class AllCellIteratorImpl : public CellIteratorImpl
{
private:
size_t m_cellIdx;
public:
AllCellIteratorImpl()
: m_cellIdx(0)
{}
virtual void increment(size_t offset)
{
m_cellIdx += offset;
}
virtual void getCellIdx() const
{
return m_cellIdx;
}
};
class VirtualLineIteratorImpl : public CellIteratorImpl
{
protected:
size_t virtualLineIdx;
size_t cellInVirtualLineIdx;
public:
VirtualLineIteratorImpl(size_t virtualLineIdx)
: virtualLineIdx(virtualLineIdx), cellInVirtualLineIdx(0)
{}
virtual void increment(size_t offset)
{
virtualLineIdx += offset;
}
};
class RowIteratorImpl : public VirtualLineIteratorImpl
{
public:
using VirtualLineIteratorImpl::VirtualLineIteratorImpl;
virtual size_t getCellIdx() const
{
// TODO: does not compile
//return mivTSudoku::getCellIdxInRow(virtualLineIdx, cellInVirtualLineIdx);
return mivTSudoku::getCellIdxInRow(VirtualLineIteratorImpl::virtualLineIdx, VirtualLineIteratorImpl::cellInVirtualLineIdx);
}
};
typedef std::bidirectional_iterator_tag CellIterator_tag;
typedef std::iterator<CellIterator_tag, Cell> CellIteratorBase;
class Cellterator : public CellIteratorBase
{
private:
typedef CellIteratorBase baseclass;
protected:
mivTSudoku* pSudoku;
CellIteratorImpl* m_pIterImpl;
public:
Cellterator(mivTSudoku* pSudoku, CellIteratorImpl* impl) noexcept
: pSudoku(pSudoku), m_pIterImpl(impl)
{
}
~Cellterator()
{
delete m_pIterImpl;
m_pIterImpl = nullptr;
}
Cellterator(const Cellterator& rhs) noexcept
: pSudoku(pSudoku), m_pIterImpl(rhs.m_pIterImpl->clone())
{}
Cellterator(Cellterator&& rhs) noexcept
: pSudoku(pSudoku), m_pIterImpl(rhs.m_pIterImpl)
{
rhs.m_pIterImpl = nullptr;
}
Cellterator& operator=(const Cellterator& rhs) noexcept
{
if (this == &rhs) return *this;
this->pSudoku = rhs.pSudoku;
this->m_pIterImpl = rhs.m_pIterImpl->clone();
}
Cellterator& operator=(Cellterator&& rhs) noexcept
{
if (this == &rhs) return *this;
this->pSudoku = rhs.pSudoku;
this->m_pIterImpl = rhs.m_pIterImpl;
rhs.m_pIterImpl = 0;
}
size_t getCellIdx() const
{
return m_pIterImpl->getCellIdx();
}
typedef typename baseclass::reference reference;
reference operator*()
{
return pSudoku->m_field[getCellIdx()];
}
reference const operator*() const
{
return pSudoku->m_field[getCellIdx()];
}
typedef typename baseclass::pointer pointer;
pointer operator->()
{
return pSudoku->m_field + getCellIdx();
}
pointer const operator->() const
{
return pSudoku->m_field + getCellIdx();
}
Cellterator& operator++()
{
m_pIterImpl->increment(1);
return *this;
}
Cellterator operator++(int)
{
Cellterator iter;
this->operator++();
return iter;
}
bool operator==(const Cellterator& rhs) const
{
return getCellIdx()==rhs.getCellIdx();
}
bool operator!=(const Cellterator& rhs) const
{
return !operator==(rhs);
}
};
public:
static const size_t dim = n*m;
static const size_t ncells = dim*dim;
private:
Cell m_field[dim];
};
typedef mivTSudoku<3,3> mivSudoku;
#endif // MIVSUDOKU_H
Can some explain to me why?
The issue is that names not depending on a template argument are looked up only when the template arguments are not known. Since your base class depends on a template argument (implicitly by being nested inside a template) the compiler diesn't look at the base class, yet: until the template is instantiated it could get specialuzed, resulting in a completely different class.
The fix is to make the reference to the base member depend on the template argument, e.g., by using this->virtualLineIdx.
I'm attempting to re-create the any that is found in Boost::any and I have built the three classes, however, whenever I come to reinterpret_cast for the value that is given, the output is completely different and just throws out garbage. Here is my code below:
namespace Types {
class PlaceMaker {
public:
PlaceMaker() { };
virtual ~PlaceMaker()
{
}
virtual PlaceMaker * clone()
{
return 0;
}
virtual const std::type_info & type() const = 0;
protected:
};
template<typename T>
class holder : public PlaceMaker {
public:
holder(const T & value)
: held(value)
{
}
virtual const std::type_info & type() const
{
return typeid(T);
}
virtual PlaceMaker * clone() const
{
return new holder(held);
}
T retrunHeld() const {
return held;
}
public:
T held;
//holder &operator=(const holder &) const = 0;
holder & operator=(const holder &) { }
};
class Any : PlaceMaker {
public:
Any() : maker(0) { };
template<typename ValueType>
Any(const ValueType & value)
: maker(new holder<ValueType>(value))
{
}
Any(const Any & other)
: maker(other.maker ? other.maker->clone() : 0)
{
}
Any& swap(Any &rhs) {
std::swap(maker, rhs.maker);
return *this;
}
template<typename ValueType>
Any & operator=(const ValueType & rhs)
{
Any(rhs).swap(*this);
return *this;
}
Any & operator=(Any rhs)
{
rhs.swap(*this);
return *this;
}
bool empty() const
{
return !maker;
}
const std::type_info & type() const
{
return maker ? maker->type() : typeid(void);
}
template<typename T>
T& cast() {
T* r = reinterpret_cast<T*>(maker);
return *r;
}
public:
PlaceMaker * maker;
};
In main I have the following:
int main() {
Types::Any a = 10;
std::cout << a.cast<int>();
}
// output: 96458224
Could anyone tell me as to where I'm going wrong?
Thanks
You're casting a Holder* to a T*. Given that Holder has virtual functions in it, that means you're gonna be looking at the vtable, not the T itself.
Basically i just want to do an arbitrary operation using given arguments of arbitrary types.
Argument type base class is Var, and Operation is base class of the operation that will executed for given arguments.
I have Evaluator class, that hold a collection of operators which mapped using opId. Evaluator will do operation based on opId argument given in evaluate() member function, then evaluate() function will do search for supported operator that will accept argument type and opId.
what I want to ask is, is there any efficient pattern or algorithm that will do this without dynamic_cast<> and/or looping through operator collection.
`
class Var {
public:
bool isValidVar();
static Var invalidVar();
}
template<typename T> class VarT : public Var {
public:
virtual const T getValue() const;
}
class Operator {
public:
virtual Var evaluate(const Var& a, const Var& b) = 0;
}
template<typename T> class AddOperator : public Operator {
public:
virtual Var evaluate(const Var& a, const Var& b)
{ //dynamic_cast is slow!
const VarT<T>* varA = dynamic_cast<const VarT<T>*>(&a);
const VarT<T>* varB = dynamic_cast<const VarT<T>*>(&b);
if(varA && varB) //operation supported
{
return VarT<T>(varA->getValue() + varA->getValue());
}
return Var::invalidVar(); //operation for this type is not supported
}
}
class Evaluator {
private:
std::map<int,std::vector<Operator>> operatorMap;
public:
virtual Var evaluate(const Var& a, const Var& b,int opId)
{
std::map<int,std::vector<Operator>>::iterator it = this->operatorMap.find(opId);
if(it != this->operatorMap.end())
{
for(size_t i=0 ; i<it->second.size() ; i++)
{
Var result = it->second.at(i).evaluate(a,b);
if(result.isValidVar())
{
return result;
}
}
}
//no operator mapped, or no operator support the type
return Var::invalidVar();
}
}
`
if you do not want to use dynamic_cast, consider adding type traits into your design.
Added 05/03/10 : The following sample will demonstrate how runtime-traits works
CommonHeader.h
#ifndef GENERIC_HEADER_INCLUDED
#define GENERIC_HEADER_INCLUDED
#include <map>
#include <vector>
#include <iostream>
// Default template
template <class T>
struct type_traits
{
static const int typeId = 0;
static const int getId() { return typeId; }
};
class Var
{
public:
virtual ~Var() {}
virtual int getType() const = 0;
virtual void print() const = 0;
};
template<typename T>
class VarT : public Var
{
T value;
public:
VarT(const T& v): value(v) {}
virtual int getType() const { return type_traits<T>::getId(); };
virtual void print() const { std::cout << value << std::endl; };
const T& getValue() const { return value; }
};
class Operator
{
public:
virtual ~Operator() {}
virtual Var* evaluate(const Var& a, const Var& b) const = 0;
};
template<typename T>
class AddOperator : public Operator
{
public:
virtual Var* evaluate(const Var& a, const Var& b) const
{
// Very basic condition guarding
// Allow operation within similar type only
// else have to create additional compatibility checker
// ie. AddOperator<Matrix> for Matrix & int
// it will also requires complicated value retrieving mechanism
// as static_cast no longer can be used due to unknown type.
if ( (a.getType() == b.getType()) &&
(a.getType() == type_traits<T>::getId()) &&
(b.getType() != type_traits<void>::getId()) )
{
const VarT<T>* varA = static_cast<const VarT<T>*>(&a);
const VarT<T>* varB = static_cast<const VarT<T>*>(&b);
return new VarT<T>(varA->getValue() + varB->getValue());
}
return 0;
}
};
class Evaluator {
private:
std::map<int, std::vector<Operator*>> operatorMap;
public:
void registerOperator(Operator* pOperator, int iCategory)
{
operatorMap[iCategory].push_back( pOperator );
}
virtual Var* evaluate(const Var& a, const Var& b, int opId)
{
Var* pResult = 0;
std::vector<Operator*>& opList = operatorMap.find(opId)->second;
for ( std::vector<Operator*>::const_iterator opIter = opList.begin();
opIter != opList.end();
opIter++ )
{
pResult = (*opIter)->evaluate( a, b );
if (pResult)
break;
}
return pResult;
}
};
#endif
DataProvider header
#ifdef OBJECTA_EXPORTS
#define OBJECTA_API __declspec(dllexport)
#else
#define OBJECTA_API __declspec(dllimport)
#endif
// This is the "common" header
#include "CommonHeader.h"
class CFraction
{
public:
CFraction(void);
CFraction(int iNum, int iDenom);
CFraction(const CFraction& src);
int m_iNum;
int m_iDenom;
};
extern "C" OBJECTA_API Operator* createOperator();
extern "C" OBJECTA_API Var* createVar();
DataProvider implementation
#include "Fraction.h"
// user-type specialization
template<>
struct type_traits<CFraction>
{
static const int typeId = 10;
static const int getId() { return typeId; }
};
std::ostream& operator<<(std::ostream& os, const CFraction& data)
{
return os << "Numerator : " << data.m_iNum << " # Denominator : " << data.m_iDenom << std::endl;
}
CFraction operator+(const CFraction& lhs, const CFraction& rhs)
{
CFraction obj;
obj.m_iNum = (lhs.m_iNum * rhs.m_iDenom) + (rhs.m_iNum * lhs.m_iDenom);
obj.m_iDenom = lhs.m_iDenom * rhs.m_iDenom;
return obj;
}
OBJECTA_API Operator* createOperator(void)
{
return new AddOperator<CFraction>;
}
OBJECTA_API Var* createVar(void)
{
return new VarT<CFraction>( CFraction(1,4) );
}
CFraction::CFraction() :
m_iNum (0),
m_iDenom (0)
{
}
CFraction::CFraction(int iNum, int iDenom) :
m_iNum (iNum),
m_iDenom (iDenom)
{
}
CFraction::CFraction(const CFraction& src) :
m_iNum (src.m_iNum),
m_iDenom (src.m_iDenom)
{
}
DataConsumer
#include "CommonHeader.h"
#include "windows.h"
// user-type specialization
template<>
struct type_traits<int>
{
static const int typeId = 1;
static const int getId() { return typeId; }
};
int main()
{
Evaluator e;
HMODULE hModuleA = LoadLibrary( "ObjectA.dll" );
if (hModuleA)
{
FARPROC pnProcOp = GetProcAddress(hModuleA, "createOperator");
FARPROC pnProcVar = GetProcAddress(hModuleA, "createVar");
// Prepare function pointer
typedef Operator* (*FACTORYOP)();
typedef Var* (*FACTORYVAR)();
FACTORYOP fnCreateOp = reinterpret_cast<FACTORYOP>(pnProcOp);
FACTORYVAR fnCreateVar = reinterpret_cast<FACTORYVAR>(pnProcVar);
// Create object
Operator* pOp = fnCreateOp();
Var* pVar = fnCreateVar();
AddOperator<int> intOp;
AddOperator<double> doubleOp;
e.registerOperator( &intOp, 0 );
e.registerOperator( &doubleOp, 0 );
e.registerOperator( pOp, 0 );
VarT<int> i1(10);
VarT<double> d1(2.5);
VarT<float> f1(1.0f);
std::cout << "Int Obj id : " << i1.getType() << std::endl;
std::cout << "Double Obj id : " << d1.getType() << std::endl;
std::cout << "Float Obj id : " << f1.getType() << std::endl;
std::cout << "Import Obj id : " << pVar->getType() << std::endl;
Var* i_result = e.evaluate(i1, i1, 0); // result = 20
Var* d_result = e.evaluate(d1, d1, 0); // no result
Var* f_result = e.evaluate(f1, f1, 0); // no result
Var* obj_result = e.evaluate(*pVar, *pVar, 0); // result depend on data provider
Var* mixed_result1 = e.evaluate(f1, d1, 0); // no result
Var* mixed_result2 = e.evaluate(*pVar, i1, 0); // no result
obj_result->print();
FreeLibrary( hModuleA );
}
return 0;
}
If you can modify the type Var you could add type-Ids for the argument types. But in the implementation of your operations you would always have to use a dynamic_cast at some point. If your types and operations are fixed at compile-time, you can do the whole thing with templates using Boost.MPL (specifically the containers).
Your sample code contains many errors, including slicing problems.
I'm not 100% sure, but I seem to remember you can use const type_info* as a key for a map.
If so, you could use something like following. It is not free from RTTI (type_info), but since Evaluator already checks the typeids, you can use a static_cast instead of a dynamic_cast (but it isn't that important now that the code doesn't blindly search for the right operator to apply).
Of course, the following is completely broken in terms of memory management. Reimplement with smart pointers of your choice.
#include <map>
#include <typeinfo>
#include <cassert>
#include <iostream>
struct CompareTypeinfo
{
bool operator()(const std::type_info* a, const std::type_info* b) const
{
return a->before(*b);
}
};
class Var {
public:
virtual ~Var() {}
virtual const std::type_info& getType() const = 0;
virtual void print() const = 0;
};
template<typename T> class VarT : public Var {
T value;
public:
VarT(const T& v): value(v) {}
const T& getValue() const { return value; }
virtual const std::type_info& getType() const { return typeid(T); }
virtual void print() const { std::cout << value << '\n'; }
};
class Operator {
public:
virtual ~Operator() {}
virtual Var* evaluate(const Var& a, const Var& b) const = 0;
virtual const std::type_info& getType() const = 0;
};
template<typename T> class AddOperator : public Operator {
public:
typedef T type;
virtual const std::type_info& getType() const { return typeid(T); }
virtual Var* evaluate(const Var& a, const Var& b) const
{
//it is the responsibility of Evaluator to make sure that the types match the operator
const VarT<T>* varA = static_cast<const VarT<T>*>(&a);
const VarT<T>* varB = static_cast<const VarT<T>*>(&b);
return new VarT<T>(varA->getValue() + varB->getValue());
}
};
class Evaluator {
private:
typedef std::map<const std::type_info*, Operator*, CompareTypeinfo> TypedOpMap;
typedef std::map<int, TypedOpMap> OpMap;
OpMap operatorMap;
public:
template <class Op>
void registerOperator(int opId)
{
operatorMap[opId].insert(std::make_pair(&typeid(typename Op::type), new Op));
}
Var* evaluate(const Var& a, const Var& b,int opId)
{
OpMap::const_iterator op = operatorMap.find(opId);
if (op != operatorMap.end() && a.getType() == b.getType()) {
TypedOpMap::const_iterator typed_op = op->second.find(&a.getType());
if (typed_op != op->second.end()) {
//double-checked
assert(typed_op->second->getType() == a.getType());
return typed_op->second->evaluate(a, b);
}
}
return 0;
}
};
int main()
{
Evaluator e;
e.registerOperator<AddOperator<int> >(0);
e.registerOperator<AddOperator<double> >(0);
VarT<int> i1(10), i2(20);
VarT<double> d1(2.5), d2(1.5);
VarT<float> f1(1.0), f2(2.0);
Var* i_result = e.evaluate(i1, i2, 0);
Var* d_result = e.evaluate(d1, d2, 0);
Var* f_result = e.evaluate(f1, f2, 0);
Var* mixed_result = e.evaluate(i1, d2, 0);
assert(i_result != 0);
assert(d_result != 0);
assert(f_result == 0); //addition not defined for floats in Evaluator
assert(mixed_result == 0); //and never for mixed types
i_result->print(); //30
d_result->print(); //4.0
}