Reputation: 3672
I've found myself writing some repetitious code in C++. I'm using some auto-generated, such that if I want to deal with Foo, Bar, & Baz they all have fairly similar method. E.g., get_foo, get_bar, get_baz, etc.
For each "thing" I more or less have to do the same types of thing. Check if it exists, if it does, get the log, look for the most recent entry in the log, check the entry for problems, etc.
That's lead to a fair bit of repeated code, along the lines of:
if (obj->has_foo) {
if(obj->get_foo().has_log()) {
Log *l = obj->get_foo().get_foo_log();
if (!l) {
ERROR("Foo does not have a log")
}
... 30-40 more lines of stuff ...
}
}
if (obj->has_bar) {
if(obj->get_bar().has_log()) {
Log *l = obj->get_bar().get_bar_log();
if (!l) {
ERROR("Bar does not have a log")
}
... 30-40 more lines of stuff ...
}
}
if (obj->has_baz) {
if(obj->get_baz().has_log()) {
Log *l = obj->get_baz().get_baz_log();
if (!l) {
ERROR("Baz does not have a log")
}
... 30-40 more lines of stuff ...
}
}
Is there a way I could build a collection, such that each item in the collection would have the unique aspects of Foo, Bar, Baz and I could use them in a single block of code.
Forgive the Perl-eese, but something like:
foreach my $thingie ("foo", "bar", "baz") {
if (obj->has_$thingie) {
if(obj->get_$thingie().has_log()) {
Log *l = obj->get_$thingie().get_$thingie_log();
if (!l) {
ERROR(sprintf("%s does not have a log", $thingie))
}
... 30-40 more lines of stuff ...
}
}
}
Or, if that's not the right direction, how do I avoid copy/pasting/tweaking that same fundamental block 3 times?
Upvotes: 1
Views: 1286
Reputation: 6516
Could you please explain your problem a a bit more Can obj conatain All three at a time:' i.e all three condition can be true 1) if (obj->has_foo) 2) if (obj->has_bar) 3) if (obj->has_baz)
I would suggest you to write a base class having common functionality. Drive all three foo,bar adn baz from that base class, Now using base class pointer for executing your calls. S So you need not to repeat code for all three.
Upvotes: 0
Reputation: 264729
C++ does not support dynamic methods.
All the methods are strictly defined at compile time.
But the 30/40 lines of code can be done with a function call:
if (!l)
{
ERROR("Bar does not have a log");
return;
}
//... 30-40 more lines of stuff ...
LogStuff(*l);
You are obviously using C++ in a way that seems natural to you: So I have a couple of questions:
Upvotes: 0
Reputation: 279445
Sure (code untested, I might have missed some problem with member pointers and type deduction, or I might just have left bugs):
template <typename T, typename M, typename F, typename G>
void doChecks(T *obj, M has_member, F get_fn, G getlog_fn) {
if (obj->*has_member) {
if (obj->*get_fn().has_log()) {
Log *l = obj->*get_fn().*getlog_fn();
if (!l) {
ERROR("%s does not have a log", typeid(T).name());
}
}
}
}
MyObj obj;
doChecks(obj, &MyObj::has_foo, &MyObj::get_foo, &Foo::get_foo_log);
doChecks(obj, &MyObj::has_bar, &MyObj::get_bar, &Bar::get_bar_log);
doChecks(obj, &MyObj::has_baz, &MyObj::get_baz, &Baz::get_baz_log);
Obviously you could use a functor-type template parameter, but this is the closest to the perl approach without actually building a dictionary into the object and rolling your own dispatch. You could also macro-ise those calls to doChecks if you wanted, and use some token-pasting to make them a bit shorter:
#define DOCHECKS(obj, class, thing) doChecks(obj, &MyObj::has_##thing, &MyObj::get_##thing, & class :: get_##thing##log)
DOCHECKS(obj, Foo, foo);
DOCHECKS(obj, Bar, bar);
DOCHECKS(obj, Baz, baz);
With additional preprocessor wizardry you can probably make it a loop, not sure. Look at the Boost preprocessor library or the Chaos Preprocessor.
Upvotes: 12
Reputation: 4595
I think you'll need to roll your own indirection, possibly using std::map to store keys of "foo", "bar", and "baz" with associated values being objects with "get_log" methods (assuming you don't need a get_foo_log() on an object returned by get_foo()). In your Perl-eese, it might change to be the following:
foreach my $thingie ("foo", "bar", "baz") {
if (obj->has($thingie)) {
if (obj->get($thingie).has_log()) {
Log *l = obj->get($thingie).get_log();
if (!l) {
ERROR(...)
}
... more lines ...
}
}
}
Upvotes: 2