?
Solved

Using shared objects for memberfunctions of a class in C++

Posted on 2003-03-06
10
Medium Priority
?
285 Views
Last Modified: 2010-04-01
hi,
i need to dynamically link the member functions of a class in my project on solaris system.
For this if i use dlsym() to get symbol of member function, it returns an error.
please help how i can use it
thanks
sunil
0
Comment
Question by:sunilarora
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 3
  • 2
10 Comments
 
LVL 12

Expert Comment

by:Salte
ID: 8082156
Probably you get an error because one of the following:

1. The name is incorrect.

If you have:

class X {
public:
   void foo();
};

Then the name of that function to the linker and for dlsym is NOT 'foo'. Trying to load the function named "foo" will fail. C++ mangles names so that the name of the function includes both parameter list (here "no args") and the class name and also other things. For example if you had another member function of X named void foo() const; it works because that function gets a different name than the function I declared above.


So the name will be something like: 1X3fooV. The letter V might be another letter on your compiler and the ordering of things may differ. Also, the components of the name are encoded with length followed by the name, this might also be different on your system.

However, all in all the name is very different from "foo", so if you want to get the address of this function you cannot use "foo" in the argument list.

Run nm or objdump on your .so file or .dll file to see what the actual name of the function is.

2. The type is incorrect.

The type of the function foo above is NOT:

void ()();

The type is:

void (X::)()

so, therefore the pointer to that function is:

void (X::*)();

so:

void (X::* ptr)();

ptr = (void (X::*)()) address_from_dl("mangled_name");

would be correct, now you can call the function if you have an object of type X:

X x;

x.*ptr(); // call the DLL!

On the whole it isn't worth the effort, it is much easier to just use:

extern "C" {
   void call_foo(X * xp);
};

and export that function call_foo from the DLL.

The dll then implements:

void call_foo(X * xp)
{ xp -> foo(); }

This gives a LOT easier interface to the user of the DLL. You can even call the function from C code using that interface.

Note that call_foo() is referenced as "call_foo" and is a regular C function so dlsym has no problems to handle it without mangling or anything.

Alf
0
 

Author Comment

by:sunilarora
ID: 8087075
Hi Alf!!!

Thanx for ur help. I tried using the first option. I probably did not consider that
C++ mangles the function names.

I tried this test program –

The header file foo.h :-
/*******************************************/
class Test{
                int a;
                int b;

            public:
                int pub;
                int func(int x);
                void normal();
        };

typedef  int (Test::*PF)(int); // pointer to a member function of class test.
/**************************************/


The function test :: func was defined in a file foo.cpp which was made into a
Shared library libfoo.so

I used the command "CC -G -KPIC foo.c -o libfoo.so" to create shared object.


And in the main prog :-
/*********************************/
PF fptr;

fptr = ( PF ) (dlsym(handle, "__1cETestEfunc6Mi_i_"));
// where this symbol was got from the objdump of the .so file.
/*********************************/

But on compiling this using ‘CC -L. try.cpp -ldl’ on my Solaris machine the compiler
Gives an error ‘can not cast from void* to int(Test::*)(int)’
It seems I cannot cast a symbol (or another function pointer) to a
pointer to a class’s method. Could u help me in this regard?

Actually what I am looking for is a way to build an application, which I can
upgrade without shutting it down. Shared libraries seemed to me to be the best
option for doing this. Using this I can replace my existing .so files as patches.
But I am having trouble using a class’s method as shared libraries.

Thanks
Sunil


0
 
LVL 12

Expert Comment

by:Salte
ID: 8087233
The problem here is that pointer to member function isn't like other pointers. It is possible they simply don't allow for it.

consider:

class X {
private:
   int x;
public:
   int foo(int y) const
   { return y + x; }
};

This is a semi-typical C++ class.

Now, if we for a moment ignore the access modifiers then this is really nothing more than good old plain C code:

struct X {
   int x;
};

int X_foo(const X * this, int y)
{ return y + this -> x; }

Now, a pointer to member would be something like:

int (X::* pf)(int y) const;

pf = X::foo;

and you would call it like in:

X a;

int z = a.*pf(2);

What is going on here?

Let's see how it would be on the C side of things:

The pointer to member is a pointer to function but it will look like this:

int (* pf)(const X * this, int y);

Note the extra argument. This isn't just any argument but it is the 'this' pointer. Some C++ compilers just treat this like any argument and have functions with this pointer simply be functions where first argument is the implicite this pointer. Other compilers uses a specific register or other special location for the 'this pointer' and so won't transfer it as an argument at all but will instead just set that location before the call.

The end result is that pointer to member functions are different form other functions and it is possible that C++ doesn't directly allow you to translate a pointer to member  from void *.

It is kinda hard to understand why it should disallow it really, since if you want to, you can make some really hairy casts that can be far more dangerous than casing a void * to a pointer to member function. It could be that the size is different but it isn't. The pointer to member function is - in the hardware - just a regular pointer like a function pointer. It is not like the Borland C++ builder __closure which really is a struct with two pointers or like the C# delegate which is a complex object all by itself. A pointer to member is really just a pointer but it is a pointer with special semantics attached to it.

Still, there are many pointers with special semantics attached to them so there's not really any good argument why C++ should disallow such cast. However, it all depends on what cast you did.

static_cast<>() will most probably not work, don't even try it.

const_cast<>() will absolutely not work, don't even try that either.

dynamic_cast<>() doesn't apply to this situation, don't try that either.

() C style cast is a bad choice, I can very well understand that a C++ construct won't go through a C style cast like this.

reinterpret_cast<>() ought to work, if they have blocked that I really don't see why they should. I can't see a good argument in favor of blocking it.

However, you could try this:

step 1.

#include <stdio.h>

struct X {
   void foo() {}
};

int main()
{
   printf( "Sizeof ptr to mbr is %d\n",
         sizeof(void (X::*)()));
   printf( "sizeof void ptr is %d\n", sizeof(void *));
}

Compile and run that program if the two numbers are the same then we are on the right track, if they aren't I should reconsider what I think I know about pointers to members. However, as far as I know they do NOT store pointers to objects and the type of the this pointer is encoded in the pointer to member type so they don't store that either.

If they are the same simply write:
typedef  int (Test::*PF)(int);

union {
   void * foo;
   PF p;
};

foo = dlsym(handle, "__1cETestEfunc6Mi_i_");
// p is now the pointer to member.

int x = p(5); // or whatever.

As you can see, there's little point in disallowing the cast, it's easy enough to get around it. For this to work the two pointers must be the same size and that is why you should do step 1 first. However, I am 99.99% sure that they are the same size so I am really convinced it should work.

It surprises me if

PF pfunc = reinterpret_cast<PF>(dlsym(....));

doesn't work though, it really should.

Alf
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 

Author Comment

by:sunilarora
ID: 8087785
hi alf!!,
The size of pointer to member function of class : 8 bytes,
whereas the size of pointer to normal function is : 4 bytes.
So it is not possible to use Union of the two pointer to access either of the two in a simple way...

also as i told earlier,

ptr = (void (X::*)()) address_from_dl("mangled_name");
is not working ...

and currently i am using the way u suggested earlier,
create a C function for each member function of any class,
in foo.c //shared object

extern "C" {
  void call_foo(X * xp);
};

and export that function call_foo from the DLL.

The dll then implements:

void call_foo(X * xp)
{ xp -> foo(); }



This flow is working, but hitch is that as a C++ programmer, this makes my code a bit less readable because one simple member function call will not be clear to others unless it is well explained to them...
but thanks for ur guidence,

sunil
0
 

Author Comment

by:sunilarora
ID: 8088488
hi Alf!!,
still it is unresolved as how to put constructor and destructor of a class as a "shared object",
hope u will put some light on this issue,
thanking u in anticipation
sunil
0
 
LVL 12

Accepted Solution

by:
Salte earned 300 total points
ID: 8089598
HMm...so size of pointer to member is 8?

That implies it is a closure_ type of pointer, which includes a pointer to the object.

so, the elements may occur in a different order, but a pointer to member such as

int (X::*)(double x);

contains the two elements:

struct ptm {
   X * obj;
   int (*)(X * this, double x);
};

This explains why the cast won't work. I can really only see one way out of this and that is to go via a static member function or a function outside the class.

Of course the type to the function pointer isn't exactly as I gave there but it indicates that it is a regular function pointer which points to the member function in question.

This leaves the question why Borland C++ builder uses __closure, what does __closure offer that pointer to member function doesn't? It is also surprising to me since I have  - for many years - had the impression that pointer to member functions were simpler in their construction and did not contain a reference to the object to use as this object for the function, that had to be supplied when you called...I need to recheck my C++ book....

In any case, the solution is to:

class foo {
private:
   ...
public:
   int bar(double x); // some function
   static int baz(foo * f, double x);
};

or perhaps even put baz outside of the class:

int baz(foo * f, double x)
{
   return f -> bar(x);
}

and export that function instead.

The constructor/destructor should be no problem as they are in the shared object already. Main problem is that you cannot construct objects using constructor directly. Since you load the shared object with dlsym etc you don't HAVE the class available at link time in the main program.

You can make unresolved external references but they won't be satisfied until you actually link the .so file and that is a bit late, the OS will complain long before.

The result is that you CANNOT create objects outside the dll. ALL objects of this class MUST be created in the class.

The obvious solution is to do this:

class foo {
......
public:
   static foo * makefoo(....);
   static void killfoo(foo * f);
};

or place them outside of foo:

foo * make_foo(....)
{
   return new foo(....);
}

and

void kill_foo(foo * f)
{
   delete f;
}

and export make_foo and kill_foo to your main program. These are regular static functions which you can get pointers to in your main program.

Note that this indicates that those objects cannot be created on stack or as global variables, they can only exist on heap.

I hope that is not a big problem for you.

Alf
0
 
LVL 11

Expert Comment

by:bcladd
ID: 9889611
No comment has been added lately, so it's time to clean up this TA. I will
leave a recommendation in the Cleanup topic area that this question is:

Answered: Points to Salte: Grade A

Please leave any comments here within the next seven days.

Experts: Silence means you don't care. Grading recommendations are made in light
of the posted grading guidlines (http://www.experts-exchange.com/help.jsp#hi73).

PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!

-bcl (bcladd)
EE Cleanup Volunteer
0
 
LVL 11

Expert Comment

by:bcladd
ID: 9895883
I will post to Community Support to have my answer unaccepted.
sunilalora: Please don't accept my answers here since I did not participate in the original question at all. Feel free to accept any of the
other answers or leave this to be cleaned up by the moderator next week.
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction This article is the first in a series of articles about the C/C++ Visual Studio Express debugger.  It provides a quick start guide in using the debugger. Part 2 focuses on additional topics in breakpoints.  Lastly, Part 3 focuses on th…
What is C++ STL?: STL stands for Standard Template Library and is a part of standard C++ libraries. It contains many useful data structures (containers) and algorithms, which can spare you a lot of the time. Today we will look at the STL Vector. …
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

765 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question