RTTI versus a base class enum to represent type

B

BillyO

I need a general mechanism for passing parameters to an API function I
am designing, call it foo. Foo processes data, the operation used to
process the data and the parameters for that operation are to be
passed to foo. A simple approach involves the use of base and derived
classes:

Class DataProcessor
{
Public:
enum ProcessorTypeEnum{op1, op2, op3}
DataProcessor(ProcessorTypeEnum
ProcType):m_ProcType(ProcType){}
ProcessorTypeEnum m_ProcType;
}

Class DataProcessorOp1
{
Public:
DataProcessorOp1(int Param1):m_Param1(Param1),
DataProcessor(op1){}
Int m_Param1;
}

And foo:

Foo(const DataProcessor& dp)
{
If (dp.m_ProcType==op1)
{
// cast dp to DataProcessorOp1 and continue
}
Else if (....=op2)
{
Etc
}
}

This is extensible since I can add new derived classes which contain
the necessary parameters as class members.

An alternate approach is to make DataProcessor polymorphic and use
RTTI to distinguish between dynamic types in Foo. I think the two are
equivalent in terms of storage, but what are the pros and cons of
both? Or is their a better way entirely?

TIA
 
P

Peter van Merkerk

BillyO said:
I need a general mechanism for passing parameters to an API function I
am designing, call it foo. Foo processes data, the operation used to
process the data and the parameters for that operation are to be
passed to foo. A simple approach involves the use of base and derived
classes:

Class DataProcessor
{
Public:
enum ProcessorTypeEnum{op1, op2, op3}
DataProcessor(ProcessorTypeEnum
ProcType):m_ProcType(ProcType){}
ProcessorTypeEnum m_ProcType;
}

Class DataProcessorOp1
{
Public:
DataProcessorOp1(int Param1):m_Param1(Param1),
DataProcessor(op1){}
Int m_Param1;
}

And foo:

Foo(const DataProcessor& dp)
{
If (dp.m_ProcType==op1)
{
// cast dp to DataProcessorOp1 and continue
}
Else if (....=op2)
{
Etc
}
}

This is extensible since I can add new derived classes which contain
the necessary parameters as class members.

Not really because extending the class hierarchy with another DataProcessor
class means that both the base class and the Foo() function need to be
modified. Generally you want to avoid this, especially with API functions.
An alternate approach is to make DataProcessor polymorphic and use
RTTI to distinguish between dynamic types in Foo. I think the two are
equivalent in terms of storage, but what are the pros and cons of
both? Or is their a better way entirely?

Difficult to say what is best without more context. For example in which way
are the DataProcessor classes different? Do they share a common interface?

To me, your enum idea does not look like a good idea for the reasons stated
above. There may be a good reason resort to enums and using casts, but I
don't see one in your post. When you need to cast to get the job done, it is
often a indication you need to reconsider your design. A good design rarely
needs casts.

If all DataProcessor classes share the same interface you can do away with
the casting and the enums.

For example:

class DataProcessor
{
public:
virtual int process(int x) = 0;
};

class DataProcessor1 : public DataProcessor
{
public:
virtual int process(int x)
{ // Perform OP1
return x*x;
}
};

class DataProcessor2 : public DataProcessor
{
public:
DataProcessor2(int m) : m_(m){}

virtual int process(int x)
{ // Perform OP2
return x*m_;
}

private:
int m_;
};

void Foo(DataProcessor& dp)
{
int i = dp.process(42);
}

int main()
{
DataProcessor1 dp1;
Foo(dp1);

DataProcessor2 dp2(100);
Foo(dp2);
}

The advantage of this approach is that you can derive other classes from
DataProcessor without needing to change the DataProcessor class or the Foo
function. Classes derived from DataProcessor may have the necessary
parameters as class members as illustrated by the DataProcessor2 class.

Note that C++ is case sensitive; 'Class' 'If', 'Else' and 'Public' are not
C++ keywords, 'class', 'if', 'else' and 'public' are.

HTH
 
D

dslater

class DataProcessor
{
public:
DataProcessor();
virtual void DoFooProcessing()const=0;
};

class DataProcessorOp1 : public DataProcessor
{
public:
DataProcessorOp1();
virtual void DoFooProcessing()const
{
// Implement op1
}
};

class DataProcessorOp2 : public DataProcessor
{
public:
DataProcessorOp2();
virtual void DoFooProcessing()const
{
// Implement op2
}
};

void Foo(const DataProcessor &dp)
{
dp.DoFooProcessing();
}

In general, it is best in C++ to avoid switching on types.
If you find youself switching on types, you should think "virtual
function".
If you can't put data processing implementation into your
DataProcessor classes
for some reason, consider using the Double Dispatch design pattern. In
that case, you would have something like:

class FooClass
{
virtual Foo(const DataProcessorOp1 &op);
virtual Foo(const DataProcessorOp2 &op);
virtual Foo(const DataProcessorOp3 &op);
// etc
};

class DataProcessor
{
public:
DataProcessor();
virtual void DoFooProcessing(FooClass &foo)const=0;
};

class DataProcessorOp1 : public DataProcessor
{
public:
DataProcessorOp1();
virtual void DoFooProcessing(FooClass &foo)const
{
foo.Foo(*this); // this will call FooClass::Foo(const
DataProcessorOp1 &op);
}
};

class DataProcessorOp2 : public DataProcessor
{
public:
DataProcessorOp2();
virtual void DoFooProcessing(FooClass &foo)const
{
foo.Foo(*this); // this will call FooClass::Foo(const
DataProcessorOp2 &op);
}
};

void Foo(const DataProcessor &dp)
{
FooClass foo;
dp.DoFooProcessing(foo);
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top