Dealing with callbacks

N

Noah Roberts

I'm looking for ideas on approaches to dealing with a type of
problem...

Say you have a project with several thousands of lines of code that
uses a certain C API for GUI work (really doesn't matter which one, the
problem is across the board). You of course have much code in which
you are casting objects to void* in order that they can be passed
through the API into some callback function, where you will be casting
from void* back into whatever type you expect.

Here's where the fun comes in...

Much has changed in this code over the years and particular objects
that are commonly cast around have become quite large; there's a lot of
monolithic objects and superfluous inheritance that is causing
problems. You would like to split them up into different higherarchies
and interfaces so that functions that use some subset of the interface
for that object can just respond to a new interface that is composed of
that functionality. This ends up introducing MI into your code.

The question of what to cast to/from really came up a long time
ago...many years ago probably...but nobody really noticed because MI
wasn't an issue so things just kind of worked even though UB is all
over the place.

So you have tons of code that is rather careless about using
reinterpret_casts so as soon as you start changing things the way you
would like to things go up shit creek really fast. The compiler of
course won't tell you anything...you can't do an accurate cast at the
other side because the objects passed in have various differing
locations for their bases...

So, what would you do? I'm kind of at a loss as to how to go about
solving this. It doesn't help that the standard cast before I came
along and said it was bad was to use C-style casts...so finding
reinterpret_casts isn't going to be easy...

The big picture is actually quite a bit worse than what I am describing
but I'm not looking for consulting. What kind of things have people
done who've experienced something resembling the problem I find myself
in?
 
A

Alf P. Steinbach

* Noah Roberts:
I'm looking for ideas on approaches to dealing with a type of
problem...

Say you have a project with several thousands of lines of code that
uses a certain C API for GUI work (really doesn't matter which one, the
problem is across the board). You of course have much code in which
you are casting objects to void* in order that they can be passed
through the API into some callback function
No.


, where you will be casting
from void* back into whatever type you expect.
[snip]

The big picture is actually quite a bit worse than what I am describing
but I'm not looking for consulting. What kind of things have people
done who've experienced something resembling the problem I find myself
in?

It's a management decision: continue to add life-support to the old dog,
or take him to the vet, and/or get a new puppy (not as clever and
familiar initially, and requires some training)?

I have the feeling you're talking about giving the dog a blood infusion
and perhaps transplanting some organs and removing some tumors, to make
it more lively and able to do new tricks.

Ach! An old dog is an old dog. Ever heard about teaching olds new
tricks? Difficult, that is. Perhaps even as difficult as teaching the
old dog not to bark or do other undesirable things.
 
P

Pete C

Noah said:
So you have tons of code that is rather careless about using
reinterpret_casts so as soon as you start changing things the way you
would like to things go up shit creek really fast. The compiler of
course won't tell you anything...you can't do an accurate cast at the
other side because the objects passed in have various differing
locations for their bases...

could you do:
static_cast<void*>(static_cast<SomeBase*>(&complexObj))
when initiating the callback and:
dynamic_cast<ComplexType*>(static_cast<SomeBase*>(voidPtr))
on the receiving end of the callback?

The idea being that the cast to SomeBase* is always valid (perhaps you
have a CObject or something like it as a virtual base class?) and that
the dynamic_cast could be tested at runtime or at least asserted upon.

Just an idea. And I know it's still a lot of manual, error-prone work.
I can't think of any easy way to avoid it off the top of my head.

One other tip: I find that if somePtr is a pointer that was obtained
from a bad cast, accessing its typeid (eg:
assert(typeid(*somePtr).name())) will often segfault immediately rather
than letting the program limp on with undefined behavior.
 
K

kwikius

Noah said:
The big picture is actually quite a bit worse than what I am describing
but I'm not looking for consulting. What kind of things have people
done who've experienced something resembling the problem I find myself
in?

Put in a request to do an audit of your current system?

regards
Andy Little
 
D

dex

This is overkill, but part of it may come in handy should you actually
choose to try and refactor your classes using multiple inheritance.


class Derived_MI;
typedef void *Pvoid;
typedef Derived_MI* PDerived_MI;

// shim pointer class to replace any pointer
// created via the 'address of' operator
class Derived_MI_ptr
{
private:
PDerived_MI m_p;

public:
Derived_MI_ptr(PDerived_MI p = NULL) : m_p(p)
{}

Derived_MI_ptr(Derived_MI_ptr &that)
{
if (this != &that)
{
m_p = that.m_p;
}
}

// pass through for the -> operator
// but you shouldn't need this for the process of
// finding all the casts in your code
PDerived_MI operator->()
{
return m_p;
}

operator PDerived_MI()
{
//assert(!"Derived_MI* requested");
__debugbreak();
return m_p;
}

// To catch every occasion where the pointer is cast
// via either reinterpret_cast<> or (void*) casts
operator void*()
{
//assert(!"void* requested");
__debugbreak();
return (void*)m_p;
}
};

class Derived_MI : public BaseMI_1, public BaseMI_2
{
public:
// To catch at compile time every occasion
// where an address is taken of this type
Derived_MI_ptr operator &()
{
// you could also insert a debugger break
__debugbreak();
//assert(!"address of Derived_MI taken");
Derived_MI *pThis = const_cast<Derived_MI*>(this);
return Derived_MI_ptr(pThis);
}
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int _tmain(int argc, _TCHAR* argv[])
{
// Test 2
Derived_MI dmi;
Derived_MI *pdmi = &dmi;
void* vp = NULL;
//vp = reinterpret_cast<void*>(&dmi); // compile time error !
// debugbreak or assert or exception based upon your choice of
implementation
vp = (void*)&dmi;

return 0;
}
 
D

Default User

Alf P. Steinbach wrote:

I have the feeling you're talking about giving the dog a blood
infusion and perhaps transplanting some organs and removing some
tumors, to make it more lively and able to do new tricks.

Ach! An old dog is an old dog. Ever heard about teaching olds new
tricks? Difficult, that is. Perhaps even as difficult as teaching
the old dog not to bark or do other undesirable things.


Only because transfusions and new organs for old dogs are expensive,
and new puppies are cheap. If the puppy cost 10x that of patching the
old dog, you might have a different decision.

Rewriting complex systems from scratch is a costly endeavor. It might
be worth it in the long run, but it might not if it in turn is obsolete
in a few years.





Brian
 
N

Noah Roberts

Default said:
Alf P. Steinbach wrote:




Only because transfusions and new organs for old dogs are expensive,
and new puppies are cheap. If the puppy cost 10x that of patching the
old dog, you might have a different decision.

Rewriting complex systems from scratch is a costly endeavor. It might
be worth it in the long run, but it might not if it in turn is obsolete
in a few years.

I kind of thought it was a given among proffessionals that you don't
just throw away years of investment and start all over because some of
the code has gotten difficult to work with.

New puppies aren't cheap. You have to teach them not to shit in the
house...not to bark all damn night long...not to chew up the
furniture...or the neighbor's cat....you have to teach them not to go
digging around in the garbage or the flower bed...there is so much to
teach a new dog that most owners unfortunately just don't do it.

The old dog might be slow and stubborn but a new one is just so damn
irritating and the time spent teaching the new dog the same tricks is
quite extensive. Especially when the old dog has been around for a
long time and has shown itself to be quite the little money maker.

Sorry, but if your best help is, "Throw it all away," that just isn't a
lot of help. If that's what you do in the face of adversity
well....well that's your choice. I'm looking for a different kind of
help though, thanks anyway.
 
N

Noah Roberts

This is overkill, but part of it may come in handy should you actually
choose to try and refactor your classes using multiple inheritance.

Interesting idea, thanks.
 
A

Alf P. Steinbach

* Noah Roberts:
I kind of thought it was a given among proffessionals that you don't
just throw away years of investment and start all over because some of
the code has gotten difficult to work with.

You raise two issues.

First, is creating a new app or set of apps (whatever) effectively to
throw away years of investment and start all over? No, because in the
meantime there's knowledge gained, market/clients gained, and perhaps
even code that can be reused (by proper packaging), and also because it
need not be all-or-nothing, and because it need not be sudden but can be
gradual, and not the least because it can save on maintenance due to New
Improved Structure and due to More Complete Knowledge, with the
developers of the new code all present in the firm, whereas some or all
of the old ones have left or will be leaving shortly. It's all about
finding ways to can-do rather than finding ways to can't-do.

Second, is code brittleness ever a reason to ditch code? Yes. Other
reasons may include e.g. that the original OS or hardware has or will
shortly become obsolete, but other than that, there's not much reason to
ditch code /except/ that it has become brittle and "difficult to work
with": it's a/the main reason, not a "just because"-reason.


[snip]
Sorry, but if your best help is, "Throw it all away," that just isn't a
lot of help. If that's what you do in the face of adversity
well....well that's your choice. I'm looking for a different kind of
help though, thanks anyway.

That's a bit turned on the head / inside-out, and also a bit personal
and hypothetical. Now that I'm focusing on those three aspects, perhaps
you can see that it's a very emotional response? I don't know what that
added emotion means, though. Perhaps my suggestion is simply not an
option due to locked-in position of management.

It might be an idea to review common antipatterns, e.g. take a look at
<url: http://en.wikipedia.org/wiki/Anti-pattern>.

Is all that void* pointer-casting just one symptom, or is it one of a
larger family of symptoms?
 
R

Roland Pibinger

I kind of thought it was a given among proffessionals that you don't
just throw away years of investment and start all over because some of
the code has gotten difficult to work with.

It's called 'refactoring' nowadays. The idea is to improve application
code, design and architecture without changing the core functionality.
I guess that's what you want to achieve. Sometimes however it's
cheaper to re-write parts of the application.

Best wishes,
Roland Pibinger
 

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

Similar Threads


Members online

No members online now.

Forum statistics

Threads
474,142
Messages
2,570,818
Members
47,362
Latest member
eitamoro

Latest Threads

Top