views:

526

answers:

11

I have got a C function in a static library, let's call it A, with the following interface :

int A(unsigned int a, unsigned long long b, unsigned int *y, unsigned char *z);

This function will change the value of y an z (this is for sure). I use it from within a dynamic C++ library, using extern "C".

Now, here is what stune me :

  1. y is properly set, z is not changed. What I exactly mean is that if both are initialized with a (pointed) value of 666, the value pointed by y will have changed after the call but not the value pointed by z (still 666).
  2. when called from a C binary, this function works seamlessly (value pointed by z is modified).
  3. if I create a dummy C library with a function having the same prototype, and I use it from within my dynamic C++ library, it works very well. If I re-use the same variables to call A(..), I get the same result as before, z is not changed.

I think that the above points show that it is not a stupid mistake with the declaration of my variables.

I am clearly stuck, and I can't change the C library. Do you have any clue on what can be the problem ? I was thinking about a problem on the C/C++ interface, per instance the way a char* is interpreted.

Thanks in advance for any help or clue.

Edit : I finally found out what was the problem. See below my answer.

A: 

Did you try "Rebuild all"?

Lev
Yes, but always a good advice !
Barth
+1  A: 

As far as I know, long long is not part of standard C++, maybe that is the source of your problem.

quinmars
A: 

In your C++ program, is the prototype declared with extern "C"?

Greg Hewgill
Yes it is. Edited my question to add this information.
Barth
+1  A: 

dunno. Try to debug-step into A and see what happens (assembly code alert!)

You are right about the "assembly code alert", the C library doesn't contain symbols... I am not sure I will be able to understand this.
Barth
+1  A: 

Maybe you can wrap the original function in a C library that you call from your C++ library?

Based on your points 2 and 3, it seems like this could work.

If it doesn't, it gives you another debug point to find more clues - see which of your libraries the failure first pops up in, and check why 2 and 3 work, but this doesn't - what is the minimal difference?

You could also try to examine the stack that is set up by your function call in each case to check if the difference is here -- considering different calling conventions.

Tyler
+1  A: 

Step 1: Compare the pointers y and z passed from the C++ side with those received by the C function.

P.S. I don't want to sound obvious, but just double-checking here. I suppose when you say that z is modified just fine when called from a C binary, you mean that the data where z is pointing is modified just fine. The pointers y and z themselves are passed by value, so you can't change the pointers.

Alexander
Yes, when I say that Z is modified, I mean that the data pointed by z is modified.I will have a look to the pointers on both sides. thanks
Barth
+4  A: 

It looks like a difference between the the way your C library and C++ compiler is dealing with long longs. My guess is that it is that the C library is probably pre C89 standard and actually treating the 64bit long long as a 32bit long. Your C++ library is handling it correctly and placing 64bits on the call stack and hence corrupting y and z. Maybe try calling the function through *int A(unsigned int a, unsigned long b, unsigned int *y, unsigned char z), and see what you get.

Just a thought.

Shane MacLaughlin
Assuming the above is correct, without the modification proposed, the C function will see y = 0 (on little-endian architectures), and z as pointing to where the C++ y is pointing.
Alexander
Yes. Given that y is a pointer it becomes architecture depentent. My guess is the OP is using 32 bit system, such that y=0 might be better though of as y=NULL.
Shane MacLaughlin
I tried what you proposed (calling with an unsigned long) but it didn't help. Alexander : y is modified by A() and z just points to the same value as before the call.
Barth
Ok, I could well be wrong as this was just a guess. Try checking sizeof(unsigned long long) in both compilers. It should be 8 bytes in both. If not, this is your problem and you need a new prototype, otherwise it is something else.
Shane MacLaughlin
+2  A: 

This is one of those questions where there's nothing obviously wrong from what you've described, yet things aren't working the way you expect.

I think you should edit your post to give a lot more information in order to get some sensible answers. In particular, let's start with:-

  • What platform is this code for: Windows, linux, something embedded or ...?
  • What compiler is the C static library built with?
  • What compiler is the C++ dynamic library built with?
  • What compiler is the C which can successfully call the library built with?
  • Do you have a source-level debugger? If so, can you step into the C code from the C++.

Unless you're wrong about A always modifying the data pointed to by Z, the only likely cause of your problem is an incompatibility between the parameter passing conventions . The "long long" issue may be a hint that things are not as they seem.

As a last resort, you could compare the disassembled C++ calling code (which you say fails) and the C calling code (which you say succeeds), or step through the CPU instructions with the debugger (yes, really - you'll learn a good skill as well as solving the problem)

Roddy
+1  A: 

Another wild guess: are you sure you're linking against the right instance of the function in your C library? Could it be that there are several such functions available in your libraries? In C the linker doesn't care about the return type or the parameter list when deciding how to resolve a function -- only the name is important. So, if you have multiple functions with the same name...

You could programmatically verify the identity of the function. Create a C library that calls your function A with some test parameters and that works fine and that prints the pointer to function A. Link the library into your C++ app. Then print the pointer to the original A function as seen from the C++ code and compare the pointer with that seen by your C library when invoked in the same process.

Alexander
+1  A: 

Again, an obvious one, but who knows... Are you sure the C function you're invoking is stateless, meaning its output depends only on its inputs? If the function isn't stateless, then it might be that the "hidden" state is responsible for the different behavior (not changing the data pointed to by z) of the function when invoked from your C++ app.

Alexander
+1  A: 

First of all, I am very grateful to everyone for your help. Thanks to the numerous ideas and clues you gave me, I have been able to finally sort out this problem. Your advices helped me to question what I took for granted.

Short answer to my problem : The problem was that my C++ library used an old version of the C library. This old version missed the 4th argument. As a consequence, the 4th argument was obviously never changed.

I am a bit ashamed now that I realised this was the problem. However, I was misslead by the fact that my code was compiling fine. This was due to the fact that the C++ library compiled against the correct version of the C lib, but at runtime it used the old version statically linked with another library that I was using.

C++ Lib (M) ---> dyn C++ lib (N) ---> C lib (P) v.1.0
     |
     ------> C lib (P) v.1.1

(N) is a dynamic library which is statically linked with (P) version 1.0. The compiler accepted the call from (M) to the function with 4 arguments because I linked against (P) version 1.1, but at runtime it used the old version of (P).

Feel free to edit this answer or the question or to ask me to do so.

Barth
Whew! Congratulations!
Alexander
Thank you. You helped me a lot with your answers (asking if the I was linking against the right instance of the function in the C library per instance, and the others as well). Cheers !
Barth