I have the following problem which I guess I am solving incorrectly given the problem I am facing:
I have an interface I and implementations A, B, C... I want to somehow express that I can get some results from couples (f(A, A), f(B, B), f(C, C)) and so on. In other words, I want to interface I to express that 2 identical implementations can be combined to produce some results, while others can be not (you can't get any valid result from f(A, B)).
Right now I have the following:
#include <iostream>
using namespace std;
class A;
class B;
class I{
public:
virtual int f (const I &other) const = 0;
virtual int fSpecific (const A &other) const { throw runtime_error(""); };
virtual int fSpecific (const B &other) const { throw runtime_error(""); };
};
class A: public I{
public:
A(int a) : a(a) {}
int f (const I &other) const override { other.fSpecific(*this); }
int fSpecific (const A &other) const override { /*logic here*/ return a + other.a; }
int a;
};
class B: public I{
public:
B(int b1, int b2) : b1(b1), b2(b2) {}
int f (const I &other) const override { other.fSpecific(*this); }
int fSpecific (const B &other) const override { /*logic here*/ return b1*b1 + b2*b2 + other.b1*other.b1 + other.b2*other.b2; }
private:
int b1;
int b2;
};
int f(const I &a, const I &b) {
a.f(b);
}
int main()
{
cout << f(A(1), A(2)) << std::endl; // prints 3
cout << f(B(1, 2), B(3, 4)) << std::endl; // prints 30
cout << f(A(1), B(3, 4)) << std::endl; // throws an error
return 0;
}
/*and so on*/
But I guess I use a wrong architecture. as adding classes results in changing I. Are there any better solution to express this such a relation?
Your interface is indeed strange, asking for method which should not be implemented.
We don't have multiple dynamic dispatch, except with std::visit of std::variant.
So following might help:
using V = std::variant<A, B, C>;
int f(const V& v1, const V& v2) {
struct {
template <typename T1, typename T2>
int operator()(const T& t1, const T2& t2) const { throw runtime_error(""); };
int operator()(const A& a1, const A& a2) const { return a1.a + a2.a; };
int operator()(const B& b1, const B& b2) const { return b1.b1*b1.b1 + b1.b2*b1.b2 + b2.b1*b2.b1 + b2.b2*b2.b2; };
int operator()(const C& c1, const C& c2) const { return c1.c * c2.c; };
} visitor;
return std::visit(visitor, v1, v2);
}
or keeping you hierarchy:
using CV = std::variant<const A*, const B*, const C*>;
class I
{
public:
virtual ~I() = default;
virtual CV asVariant() const = 0;
};
class A: public I{
public:
A(int a) : a(a) {}
CV asVariant() const override { return this; }
friend int f (const A& a1, const A& a2) { /*logic here*/ return a1.a + a2.a; }
int a;
};
class B: public I{
public:
B(int b1, int b2) : b1(b1), b2(b2) {}
CV asVariant() const override { return this; }
friend int f (const B& b1, const B& b2) {
/*logic here*/ return b1.b1*b1.b1 + b1.b2*b1.b2 + b2.b1*b2.b1 + b2.b2*b2.b2;
}
private:
int b1;
int b2;
};
int f(const I& i1, const I& &2) {
struct {
template <typename T1, typename T2>
int operator()(const T1*, const T2*) const { throw runtime_error(""); };
template <typename T>
int operator()(const T* t1, const T* t2) const { return f(*t1, *t2); };
} visitor;
return std::visit(visitor, i1.AsVariant(), i2.AsVariant());
}
You may employ dynamic_cast:
class I {
public:
template<typename T>
void fSpecific (T &other) {
if (dynamic_cast<T*>(this))
std::cout << "OK" << std::endl;
else
std::cout << "ERROR" << std::endl;
}
virtual ~I() {}
};
class A : public I {
};
class B : public I {
};
int main()
{
A a;
a.fSpecific(a);
B b;
b.fSpecific(a);
b.fSpecific((I&)a);
return 0;
}
There are some problems however:
Multiple inheritance
Objects need to be dynamically castable (that is why I added a virtual interface)
Casting to I also works.
Related
I want to avoid duplicated code in this usecase
class A {
protected:
virtual void A1(const void* const s, const std::streamsize n) const;
inline void A2(const void* const s, const std::streamsize n) const;
};
class B : public A {
private:
const char *a;
void B1(const char *b) {
if (!b) {
return;
}
if (a < b) {
A1(a, b-a);
}
}
void B2(const char *b) {
if (!b) {
return;
}
if (a < b) {
A2(a, b-a);
};
}
};
So, as you can see above in both B1() and B2() there is duplicate code (that check for b) except for the call inside that if (note that the if condition is the same). I think this ifmakes somehow inconvenient to extract a new method, but also I think it can be done using lambdas and/or templates. There is no point of interest on how A1() and A2() are implemented for this usecase.
My question: What is the best and simplest way to avoid this duplication of code ?
You can write a function that accepts pointer to member to be executed
class B : public A {
private:
const char *a;
using F = void(A::*)(const void* const, const std::streamsize) const;
void RunFun(F f, const char *b) {
if (!b) {
return;
}
if (a < b) {
(this->*f)(a, b-a);
}
}
void B1(const char *b) {
RunFun(&B::A1,b);
}
void B2(const char *b) {
RunFun(&B::A2,b);
}
};
Another (simplified) example, using lambda and std::function
#include <cstring>
#include <iostream>
#include <functional>
struct A
{
virtual void A1 (char const * const b)
{ std::cout << b << "\n- A1 call" << std::endl; }
void A2 (char const * const b)
{ std::cout << b << "\n- A2 call" << std::endl; }
};
struct B : public A
{
const char * a;
std::function<void(char const * const, void(A::*)(char const * const))>
funcA { [this](char const * const b, void(A::*f)(char const * const))
{ if ( b && std::strlen(b) ) (this->*f)(b); } };
void B1 (char const * b)
{ funcA(b, &A::A1); }
void B2 (char const * b)
{ funcA(b, &A::A2); }
};
int main ()
{
B b;
b.B1("- B1 call");
b.B2("- B2 call");
}
I have below as 'simple' of an example of what I am trying to do as I could think up. I have an abstract class A which exposes a public interface to the world with two methods: operator== and performTasksSpecificToA. You can see that I'm using the 'Template Method Pattern' as well as the 'curiously recurring template pattern' in order to ensure that users of A don't need to worry about the implementation of A, in other words AImpl, while still being able to check equality against two instances of AImpl. See this answer on SO for a bit more information and context on this approach.
Now, suppose I wish to define a class B as follows:
class B
{
public:
virtual ~B() = 0;
bool operator(const B& b) const;
void performTasksSpecificToB();
};
As you can see, class B shares the same problem as A in terms of defining a public operator== for comparing sub-classes. How can I define a parent-class, let's call it Letter, in order to avoid duplicating code between A and B?
Here is my 'simple example', which compiles and runs.
#include <iostream>
class A
{
public:
virtual ~A() = 0;
bool operator==(const A& a) const;
void performTasksSpecificToA();
private:
virtual bool checkEquality_(const A& a) const = 0;
};
template <class T>
class A_ : public A
{
protected:
bool checkEquality_(const A& a) const override;
private:
virtual bool checkEquality(const T& t) const = 0;
};
class AImpl : public A_<AImpl>
{
public:
AImpl(int val) : val(val){};
bool checkEquality(const AImpl& anAImpl) const override;
private:
int val;
};
A::~A(){}
bool A::operator==(const A& a) const{
return checkEquality_(a);
}
template <class T>
bool A_<T>::checkEquality_(const A& a) const{
const T* other = dynamic_cast<const T*>(&a);
if (other != nullptr){
const T& me = static_cast<const T&>(*this);
return other->checkEquality(me);
}
return false;
}
bool AImpl::checkEquality(const AImpl& anAImpl) const{
return val == anAImpl.val;
}
int main(){
// factory:
AImpl* aImpl1 = new AImpl(1);
AImpl* aImpl2 = new AImpl(2);
AImpl* aImpl3 = new AImpl(1);
// client:
A& A1 = *aImpl1;
A& A2 = *aImpl2;
A& A3 = *aImpl3;
std::cout << "A1 == A2 -> ";
std::cout << (A1 == A2 ? "true" : "false");
std::cout << std::endl;
std::cout << "A1 == A3 -> ";
std::cout << (A1 == A3 ? "true" : "false");
std::cout << std::endl;
delete aImpl1;
delete aImpl2;
delete aImpl3;
return 0;
}
If you can allow Letter to be a template, you can simply have A inherit from a template base:
template<class T>
class Letter
{
public:
bool operator==(const Letter<T>& t) const {
const T& t1 = static_cast<const T&>(*this);
const T& t2 = static_cast<const T&>(t);
return t1.checkEquality_(t2);
}
private:
virtual bool checkEquality_(const T& a) const = 0;
};
class A : public Letter<A>
{
public:
virtual ~A() = 0;
void performTasksSpecificToA();
};
...
If you absolutely need a common Letter, you probably have to add another layer of CRTP like you did with A_ and A.
I have some large amounts of strings grouped in some classes, all grouped in one final giant class. This class must be filled up by another class and its immutable content exposed to some clients. (Of course, these classes are more complex, this is a simplified schematic representation.)
Solution 1:
class A
{
friend class M;
private:
B m_b;
C m_c;
D m_d;
public:
const B& GetB() const { return m_b;}
const C& GetC() const { return m_C;}
const D& GetD() const { return m_D;}
private:
B& GetB() { return m_b;}
C& GetC() { return m_C;}
D& GetD() { return m_D;}
}
where B is something like:
class B
{
friend class M;
private:
std::string m_camel;
std::string m_pink;
std::string m_vandergraaf;
public:
const std::string& Camel() const { return m_camel;}
const std::string& PinkFloyd() const { return m_pink;}
const std::string& VanDerGraafGenerator() const { return m_vandergraaf;}
private:
void SetCamel(const char* prog) { m_camel = prog;}
void SetPinkFloyd(const char* prog) { m_pink = prog;}
void SetVanDerGraafGenerator(const char* prog) { m_vandergraaf = prog;}
}
A better solution, that avoids friend for protected is to expose the write access class to M and the base one, read-only to the world.
Solution 2:
class A
{
protected:
B m_b;
C m_c;
D m_d;
public:
const B& GetB() const { return m_b;}
const C& GetC() const { return m_C;}
const D& GetD() const { return m_D;}
}
// only visible to M
class A_Write: public A
{
public:
B& GetB() { return m_b;}
C& GetC() { return m_C;}
D& GetD() { return m_D;}
}
Same thing for B, maybe. Not a very good solution since the clients can also derive their own classes.
A better but more constraint variant is solution 3:
class A
{
private:
const B m_b;
const C m_c;
const D m_d;
public:
const B& GetB() const { return m_b;}
const C& GetC() const { return m_C;}
const D& GetD() const { return m_D;}
protected:
A(const B& b, const C& c, const D& d): m_b(), m_c(c), m_d(d) {}
}
// only visible to M
class A_Write: public A
{
public:
A_Write(const B& b, const C& c, const D& d): A(b, c, d) {}
}
My preferred solution is 4, which is ... 3 but with B,C,D as simple structs instead of classes. So M can do anything it wants directly in B, C, D, then construct a A_Write.
Any better ideas ?
A possible approach could be to use a proxy that reduces the interface of your class.
The class M will instantiate/receive an instance of S (so as to be able to modify it using its interface), but it will return a proxy P to the readers (that won't manage to modify it).
It follows a minimal example:
struct S {
void value(int v) noexcept { t = v; }
int value() const noexcept { return t; }
private:
int t{0};
};
struct P {
P(S& s): b{s} { }
int value() const noexcept { return b.value(); }
private:
S& b;
};
int main() {
// the class: full interface
S s;
// the proxy: reduced interface
P p{s};
}
#include <iostream>
class A
{
public:
int a;
A() { a = 2;}
A(int f) { a= f;}
void print() { std::cout << a << std::endl; }
};
class B
{
A a, at, at2;
A& operator += (A& b)
{
a.a = a.a + b.a;
return a;
}
public:
B(int a_, int at_, int at2_) : a(a_), at(at_), at2(at2_) {};
void update ()
{
a += at;
}
void printAll() { a.print(); at.print();}
};
int main()
{
B value ( 2, 3, 5);
value.printAll();
value.update();
value.printAll();
}
The error is :
temp.cpp:24:10: error: no match for 'operator+=' in '((B*)this)->B::a += ((B*)this)->B::at'
What am I doing wrong ?
The operator you are defining is A & operator+=(B &, A & ), not A & operator+=(A &, A &). So you have defined how to add an A to a B, but not how to add an A to an A. Try this after the definition of class A but before that of class B:
A & operator+=(A & a1, const A & a2) { a1.a += a2.a; return a1; }
But this kind of operator is more natural to define as a member function.
A& B::operator += (A& b)
Means
A & operator+=(B &, A & )
You simply need to add operator +=(const A&b) to class A
class A
{
//....
A& operator += (const A& b)
{
a += b.a;
return *this;
}
//....
};
A non-member version is:
A & operator+=(A a1, const A & a2) { a1.a += a2.a; return a1; }
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
}