473,383 Members | 1,813 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,383 software developers and data experts.

passing c++ methods as c-callbacks

Hi folks,

after reading several threads on this issue (-> subject) I fear that I
got a problem that cannot easily be solved by the offered workarounds
in an acceptable way, at least not with my limited c & c++ experience.
Maybe some of you can help.

the problem:
I need several instances of a class whose (non-static!) methods should
serve as callbacks for a dll (which can' be manipulated/adapted in any
way). Static use is inacceptable and won't work for my setup because
I'm in desperate need for several independent working instances of the
same class.
The actual problem is the absence of any possibility to pass user data
to the callers of the callbacks (which are some device driver-dlls) so
that I can't use a wrapper to delegate the calls to the correct class
instance.

Is there any chance for a workaround that doesn't touch the dlls in any
way so the same class can be used for each dll? Any help would be
appreciated.

Thanks,
Dirk

Apr 26 '06 #1
9 3318
zh*******@web.de wrote:
Hi folks,

after reading several threads on this issue (-> subject) I fear that I
got a problem that cannot easily be solved by the offered workarounds
in an acceptable way, at least not with my limited c & c++ experience.
Maybe some of you can help.

the problem:
I need several instances of a class whose (non-static!) methods should
serve as callbacks for a dll (which can' be manipulated/adapted in any
way). Static use is inacceptable and won't work for my setup because
I'm in desperate need for several independent working instances of the
same class.
So, in summary, the DLL has only one function pointer (probably 4
bytes)
yet you require it to distinguish not only functions but also instances

(probably 4+4 bytes). That simply won't fit.
Is there any chance for a workaround that doesn't touch the dlls in any
way so the same class can be used for each dll? Any help would be
appreciated.


If you accept a fixed (but configurable) number of callbacks, define a
template<int N> wrapper, and store the instance pointers and member
pointers in two arrays. wrapper<i> will then retrieve the i'th instance
pointer
and member. While you can decide at runtime what wrapper<i> means,
you can't set more callbecks than the number of instantiated
wrapper<i>s.
(Of course, if you put those in your own DLLs too, you can just open
100.DLL, 200.DLL, 300.DLL to get extra wrapper functions - non
standard)

HTH
Michiel Salters

Apr 26 '06 #2
>> The actual problem is the absence of any possibility to pass user data
to the callers of the callbacks (which are some device driver-dlls) so
that I can't use a wrapper to delegate the calls to the correct class
instance. Is there any chance for a workaround that doesn't touch the dlls in any
way so the same class can be used for each dll? Any help would be
appreciated.


If I understand you correctly, you have multiple device driver dlls
that have callback routines that you must implement and you want to
associate a specific instance of one callback class with each dll?
Based on your description, I'm assuming the callbacks from the device
driver dlls are c-style functions. Also, you are unable pass a void* or
some other type of user data to the device driver that would get
returned in the callback, correct?

In this case, since you cannot pass user data to the dlls that will get
returned in the callback, you will need another way of delegating the
callbacks to C++ classes. Depending on your situation, you may want to
set up a singleton or global callback class that all the c-style
functions will know about and can use to delegate their calls to. Once
the calls are delegated to that singleton object, it would then decide
what specific instance of your final callback class to call based on
some information either known at compile time or at runtime. (You could
also set up your final callback classes as singletons themselves and
have the device driver callbacks call them directly, if there is a
different set of c-style callback for each dll.)

The singleton pattern is often overused, but in a situation like this,
it is definitely a correct choice.

Hope that helps.

--Lyell

Apr 26 '06 #3
First of all thanks a lot for your feedback Michiel and Lyell.

@Lyell:
Unfortunately your approach won't work here because only one of four
callbacks (each device driver controls the same device type and thus
has the same interface and the same four callbacks) contains
information that could help to determine the caller at run or compile
time. No chance to tell where the other callbacks originate from.

@Michiel:
It seems to be that your template approach with a fixed amount of
instances is the only thing that could serve as a solution at the
moment. It's quite frustrating though that one has to sacrifice the
advantage of dynamic scaling.

So again, thanks a lot for your help!

Bye,
Dirk

Apr 26 '06 #4
>> Unfortunately your approach won't work here because only one of four
callbacks (each device driver controls the same device type and thus
has the same interface and the same four callbacks) contains
information that could help to determine the caller at run or compile
time. No chance to tell where the other callbacks originate from.


Maybe you could write an adapter dll for each of your device drivers
and then load your adapter dll instead of loading the device driver dll
directly? There could be a generic adapter dll for any device drivers
you don't handle specifically. You'd then have control over the
callback handling and still have the ability to dynamically scale by
just writing a new adapter dll anytime you want to do something
specific for a particular device driver.

Lyell

Apr 26 '06 #5
zh*******@web.de wrote:
[snip]
The actual problem is the absence of any possibility to pass user data
to the callers of the callbacks (which are some device driver-dlls) so
that I can't use a wrapper to delegate the calls to the correct class
instance.


So, you write a configurable thunk table. Like so: You create several
functions that can be passed to the dlls as callbacks. Each of these
then looks up in a global table where to call to get the real
functionality.
Each function looks for a particular line in the table. And you fill in
that line dynamically according to what you want things to be doing.

So, the thunk table might include a pointer to an instance of a class
that does the job you want. And that instance might be derived from
a base class that has a member fcn to call. And you just derive a
class for each type of functionality you want.

So it only adds one layer of indirection. You create an instance of the
handling class and put the pointer in the table. Then you pass the
appropriate function pointer to the dll. With a little work you can
even
have the table collect the function pointers, and keep track of what
dlls they have been assigned to, and what type of class they have
had attached for doing the work. Or whatever else book keeping is
required to assist. You can make this exactly as fancy as required.

Wrap the entire thing in a singleton and you are cooking.
Socks

Apr 26 '06 #6
zh*******@web.de wrote:
The actual problem is the absence of any possibility to pass user data
to the callers of the callbacks (which are some device driver-dlls) so
that I can't use a wrapper to delegate the calls to the correct class
instance.

Is there any chance for a workaround that doesn't touch the dlls in any
way so the same class can be used for each dll? Any help would be
appreciated.


Yes. In the Lisp programming language, there is something known as a
dynamically-scoped variable. A dynamically scoped variable has a
global top-level binding, but it can be overriden locally. For instance
if X is dynamically scoped then (LET ((X 3)) ...) binds X to a new
storage location over the scope of the let block. Any function you call
from there will see X as being bound to that storage location which has
that value. X can be changed of course. When the LET block terminates,
the binding is restored to the previous binding (no matter how that
block terminates: normal exit or non-local exit via throw or whatever).

Under multithreaded implementations, these re-bindings affect only the
calling thread, so dynamic variables effectively become an abstraction
for thread-local storage.

They are useful for passing down context information through functions,
or layers of functions, which don't have the arguments for doing so.

Because of the saving-restoring discipline of the local rebinding,
dynamic variables are more disciplined than simple global variables.
Sure you rebind the variable to create an effect in some other module,
but then the old binding is restored when you are done.

You can give yourself the same thing in C++. In single-threaded code,
it can be done portably. Simply use a global variable for the context,
and use RAII to save and restore its value.

I wrote a pair of templates to do this. The template DynamicVar<T> is
used to define a dynamic variable. It's actually a class object which
contains a T, but also contains a T * pointer to the latest binding. If
that pointer is null, then the embedded T is the binding of that
variable. If that pointer is not null, then it points to the current
location of T.

The other template, RebindVar<T>, is used to temporarily bind a given
variable to a new location. Like DynamicVar<T>, it contains an embedded
T. It takes an initial value through the constructor, and a reference
to the DynamicVar<T>. It saves the original T * pointer within the
DynamicVar<T> and then chages it to point to its own embedded instance
of T. In the destructor, the pointer in the global is restored to its
previous value.

Both DynamicVar and RebindVar have the assignment and conversion
operators to transparently operate on the embedded T. RebindVar
bypasses the pointer, of course! If you have the RebindVar object in
lexical scope, then the dynamic lookup thorugh the pointer isn't
required.

So your code would look something like this:

// file scope or static class variable
DynamicVar<MyClass *> d_contextPointer;

void CallLibrary(MyClass *obj)
{
// Set up local re-binding of d_contextPointer

RebindVar<MyClass *> contextPointer(obj);
LibraryFunction(&MyClass::StaticCallback);

// scope ends, old binding is now restored
}

void MyClass::StaticCallback()
{
// retrieve context from temporary binding
d_contextPointer->NonStaticCallback();
}

If you need threading support, the DynamicVar and RebindVar templates
can hide the calls to the thread-specific storage API functions on your
platform or whatever, so that the constructor and destructor function
of RebindVar<> is visible only in the context of the calling thread.

As you can see, the scheme supports recursion nicely. The callback
itself can override the dynamic variable in a nested fashion and call
into library, resulting in another callback which can use a different
object.

Greenspun's Tenth Rule of Programming:

"Any sufficiently complicated C or Fortran program contains an ad-hoc,
informally-specified bug-ridden slow implementation of half of Common
Lisp."

Apr 26 '06 #7
>
Is there any chance for a workaround that doesn't touch the dlls in any
way so the same class can be used for each dll? Any help would be
appreciated.


The solution isn't pretty, but I believe you want what is referred to as
a 'thunk'.

The short explanation of it is to allocate and initialize a structure
with machine language opcodes and use the address of the structure as
your function pointer.

For example if you have the following definition:

class MyObject {
public:
int myMethod (int value);
};

and have an allocated instance of this object named myObject, and need
to create a callback that accomplishes the following:

int Callback1 (int value)
{
return myObject->myMethod (value);
}

Then you need to create a thunk structure like so:

struct Thunk1
{
unsigned u1; // set to opcodes for __asm mov ecx, pThis
unsigned u2; // set to opcodes for __asm push value
unsigned u3; // set to opcodes for __asm call myMethod
};

And then use the address of Thunk1 as the callback.

Obviously this sort of thing is highly unportable, requiring you to
compile a similar function for the target machine then look at the
opcodes emitted to determine how to initialize your structure, but since
we are talking about device drivers I am guessing you have some freedom
in this area.

I may have mistated a few things here, but hopefully the concept is
clear enough. I apologize for the gross hackery :)
Apr 26 '06 #8
>

So, you write a configurable thunk table. Like so: You create several
functions that can be passed to the dlls as callbacks. Each of these
then looks up in a global table where to call to get the real
functionality.
Each function looks for a particular line in the table. And you fill in
that line dynamically according to what you want things to be doing.


I want to add that I like this method a lot better than my suggestion,
but it does have the difference of requiring you to know an upper limit
to the number of objects you may need; which is very likely since we are
talking about a device driver.
Apr 26 '06 #9
Things get quite interesting and fascinating here. I think I need some
time to find my way through all your detailed concepts and
descriptions. Thanks a lot for your help and participation.

Bye,
Dirk

Apr 26 '06 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

0
by: Anthony Baxter | last post by:
To go along with the 2.4a3 release, here's an updated version of the decorator PEP. It describes the state of decorators as they are in 2.4a3. PEP: 318 Title: Decorators for Functions and...
6
by: Bryan Martin | last post by:
I have a object that is created in a seperate domain which needs to be passed back to the parent class. Because this object is created in a seperate domain if I try to pass the object back to the...
7
by: Tron Thomas | last post by:
Under the right compiler the following code: class Base { public: virtual void Method(int){} }; class Derived: public Base {
3
by: Simon Harvey | last post by:
Hi, In my application I get lots of different sorts of information from databases. As such, a lot of information is stored in DataSets and DataTable objects. Up until now, I have been passing...
25
by: Stuart Hilditch | last post by:
Hi all, I am hoping that someone with some experience developing nTier apps can give me some advice here. I am writing an nTier web app that began with a Data Access Layer (DAL), Business...
5
by: blue | last post by:
We often get connection pooling errors saying that there are no available connections in the pool. I think the problem is that we are passing around open readers all over the place. I am...
2
by: Colin McGuire | last post by:
Hi everyone. This is a question and something new and neat I have found. But to recap - a few months back I learnt about overloading, how to create the same method with a different signature. With...
4
by: shade73 | last post by:
Hey all. I currently have two seperate namespaces and I'm trying to pass a connection around to them. I want to use the same connection & leave it open for 6 methods & then close it. However, all...
9
by: Greger | last post by:
Hi, I am building an architecture that passes my custom objects to and from webservices. (Our internal architecture requires me to use webservices to any suggestion to use other remoting...
46
by: ahmed.maryam | last post by:
Hi all, I have 2 C# applications that I need to pass data between. Specifically XML information such as a document or node name. How can I do that? Thanks in advance! ~ Maryam
1
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...
0
by: ryjfgjl | last post by:
In our work, we often need to import Excel data into databases (such as MySQL, SQL Server, Oracle) for data analysis and processing. Usually, we use database tools like Navicat or the Excel import...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.