www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Passing opaque struct between functions/modules

reply "Sarath Kumar" <sarath invalid.com> writes:
DMD v2.61; openSUSE 12.1

Source:
-------
libA.d:

module libA;

extern (C)
{
         struct Opaque;

         Opaque* getObject();

         void doSomething(Opaque *);
}
----------
libB.d:

module libB;

extern (C)
{
         struct Opaque;

         void doAction(Opaque *);
}
-----------
bug.d

import libA, libB;

int main(string[] args)
{
         auto opaque = libA.getObject();
         libA.doSomething(opaque); // this is okay
         libB.doAction(opaque);  // but this is not, compiler 
error here
         return 0;
}

When I compile the above files, I get the below error.
$ rdmd bug.d
bug.d(7): Error: function libB.doAction (Opaque*) is not callable 
using argument types (Opaque*)
bug.d(7): Error: cannot implicitly convert expression (opaque) of 
type Opaque* to Opaque*

If I do an explicit cast, libB.Opaque*, for opaque in line 7, 
bug.d, I get completely different set of errors.

$ rdmd bug.d
libB.d(5): Error: struct libB.Opaque unknown size
libB.d(5): Error: struct libB.Opaque no size yet for forward 
reference
libB.d(5): Error: struct libB.Opaque unknown size
libB.d(5): Error: struct libB.Opaque no size yet for forward 
reference
libA.d(5): Error: struct libA.Opaque unknown size
libA.d(5): Error: struct libA.Opaque no size yet for forward 
reference

Can someone please tell me the right way to pass an opaque object 
between module's functions.

--
Thanks,
Sarath
Jan 23 2013
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 01/23/2013 08:33 AM, Sarath Kumar wrote:

 Can someone please tell me the right way to pass an opaque object
 between module's functions.
I am assuming that you are interfacing with a C library. That library must have a D binding file. I am assuming that it is your libA.d: // libA.d module libA; extern (C) { struct Opaque; Opaque* getObject(); void doSomething(Opaque *); } There is also a library that implements the struct and the functions. Although you would have it in the C library, here is a D module to imitate it: // libA_impl.d // These are presumably defined in a C library. Simply imitating it with this // D module extern (C) { struct Opaque { int i; double d; } Opaque* getObject() { return new Opaque(42, 1.5); } void doSomething(Opaque *) {} } Here is the D module that makes use of that library by importing its D binding: // libB.d module libB; import libA; void doAction(Opaque* o) { doSomething(o); } void main() { Opaque* o = getObject(); doAction(o); } Here is how to build the program: $ dmd libA.d libB.d libA_impl.d -ofmy_prog Note that normally libA_impl.d would not be included. Instead, the C library would be used. For example, if we are talking about a libmy_c_lib.a: $ dmd libA.d libB.d -L-lmy_c_lib -ofmy_prog Yes, with the confusing -L in there. ;) Ali
Jan 23 2013
parent "Sarath Kumar" <sarath invalid.com> writes:
On Wednesday, 23 January 2013 at 17:14:31 UTC, Ali Çehreli wrote:
 On 01/23/2013 08:33 AM, Sarath Kumar wrote:

 Can someone please tell me the right way to pass an opaque
object
 between module's functions.
I am assuming that you are interfacing with a C library. That library must have a D binding file. I am assuming that it is your libA.d: There is also a library that implements the struct and the functions. Although you would have it in the C library, here is a D module to imitate it: // libA_impl.d // These are presumably defined in a C library. Simply imitating it with this // D module extern (C) { struct Opaque { int i; double d; } Opaque* getObject() { return new Opaque(42, 1.5); } void doSomething(Opaque *) {} }
I don't know the contents of the struct as libA and libB are 3rd party C libraries.
Jan 24 2013
prev sibling next sibling parent reply "Mike Parker" <aldacron gmail.com> writes:
On Wednesday, 23 January 2013 at 16:33:08 UTC, Sarath Kumar wrote:
 DMD v2.61; openSUSE 12.1

 Source:
 -------
 libA.d:

 module libA;

 extern (C)
 {
         struct Opaque;

         Opaque* getObject();

         void doSomething(Opaque *);
 }
 ----------
 libB.d:

 module libB;

 extern (C)
 {
         struct Opaque;

         void doAction(Opaque *);
 }
 -----------
 bug.d

 import libA, libB;

 int main(string[] args)
 {
         auto opaque = libA.getObject();
         libA.doSomething(opaque); // this is okay
         libB.doAction(opaque);  // but this is not, compiler 
 error here
         return 0;
 }

 When I compile the above files, I get the below error.
 $ rdmd bug.d
 bug.d(7): Error: function libB.doAction (Opaque*) is not 
 callable using argument types (Opaque*)
 bug.d(7): Error: cannot implicitly convert expression (opaque) 
 of type Opaque* to Opaque*

 If I do an explicit cast, libB.Opaque*, for opaque in line 7, 
 bug.d, I get completely different set of errors.

 $ rdmd bug.d
 libB.d(5): Error: struct libB.Opaque unknown size
 libB.d(5): Error: struct libB.Opaque no size yet for forward 
 reference
 libB.d(5): Error: struct libB.Opaque unknown size
 libB.d(5): Error: struct libB.Opaque no size yet for forward 
 reference
 libA.d(5): Error: struct libA.Opaque unknown size
 libA.d(5): Error: struct libA.Opaque no size yet for forward 
 reference

 Can someone please tell me the right way to pass an opaque 
 object between module's functions.
The error message here is deceiving. Declare the struct in one module only and then import it in every module that uses it. So, for example, keep the declaration in libA, remove it from libB and in libB add "import libA;".
Jan 24 2013
parent reply "Sarath Kumar" <sarath invalid.com> writes:
On Thursday, 24 January 2013 at 09:04:09 UTC, Mike Parker wrote:
 The error message here is deceiving. Declare the struct in one 
 module only and then import it in every module that uses it. 
 So, for example, keep the declaration in libA, remove it from 
 libB and in libB add "import libA;".
I can't import libA into libB. So, I had moved the forward declaration into a separate D file and did a public import into libA and libB. This works. But this is a workaround. I will file a bug. -- Thanks, Sarath
Jan 24 2013
parent reply "Mike Parker" <aldacron gmail.com> writes:
On Thursday, 24 January 2013 at 09:39:39 UTC, Sarath Kumar wrote:
 On Thursday, 24 January 2013 at 09:04:09 UTC, Mike Parker wrote:
 The error message here is deceiving. Declare the struct in one 
 module only and then import it in every module that uses it. 
 So, for example, keep the declaration in libA, remove it from 
 libB and in libB add "import libA;".
I can't import libA into libB. So, I had moved the forward declaration into a separate D file and did a public import into libA and libB. This works. But this is a workaround. I will file a bug.
The only potential bug I see here is in the error message. What you're seeing is a conflict that arises from D's name mangling. The doSomething in libA is expecting a parameter of type libA.Opaque* and getObject is returning the same, whereas the functions in libB are expecting a parameter of type libB.Opaque*. You can see this if you do the following: writeln(doSomething.mangleof); writeln(doAction.mangleof); This is why your code is failing when you call getAction, because you're passing it a libA.Opaque* returned from libA.getObject. The solution is to declare opaque struct in a single place and import it everywhere you need it. That's why your public imports work. I would suggest that you not use the public import in this case, but work out some other scenario. In Derelict (a collection of C bindings) for example, I tend to follow this pattern for any lib a bind to: types.d <- all type & constant declarations go here functions.d <- all function declarations go here, imports the types module Then I have a module named according to the C library's primary header that publicly imports both. For SDL2, for example: module derelict.sdl2.sdl; public { import derelict.sdl2.types; import derelict.sdl2.functions; } This way, all types are declared in a single location so there's no danger of mixing up different mangles of the same type.
Jan 24 2013
parent "Sarath Kumar" <sarath invalid.com> writes:
On Thursday, 24 January 2013 at 11:52:57 UTC, Mike Parker wrote:
 The only potential bug I see here is in the error message. What 
 you're seeing is a conflict that arises from D's name mangling. 
 The doSomething in libA is expecting a parameter of type 
 libA.Opaque* and getObject is returning the same, whereas the 
 functions in libB are expecting a parameter of type 
 libB.Opaque*.
It is a bug because even after casting, the compiler gives an error. The compiler wants to know the size of the opaque object, even though I'm dealing with a pointer only. As Maxim Fomin mentioned, the compiler is trying to create an instance of the object even when it is declared as extern (C) and it is a pointer. -- Thanks, Sarath
Jan 24 2013
prev sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Wednesday, 23 January 2013 at 16:33:08 UTC, Sarath Kumar wrote:
 DMD v2.61; openSUSE 12.1

 Source:
 -------
 libA.d:

 module libA;

 extern (C)
 {
         struct Opaque;

         Opaque* getObject();

         void doSomething(Opaque *);
 }
 ----------
 libB.d:

 module libB;

 extern (C)
 {
         struct Opaque;

         void doAction(Opaque *);
 }
 -----------
 bug.d

 import libA, libB;

 int main(string[] args)
 {
         auto opaque = libA.getObject();
         libA.doSomething(opaque); // this is okay
         libB.doAction(opaque);  // but this is not, compiler 
 error here
         return 0;
 }

 When I compile the above files, I get the below error.
 $ rdmd bug.d
 bug.d(7): Error: function libB.doAction (Opaque*) is not 
 callable using argument types (Opaque*)
 bug.d(7): Error: cannot implicitly convert expression (opaque) 
 of type Opaque* to Opaque*

 If I do an explicit cast, libB.Opaque*, for opaque in line 7, 
 bug.d, I get completely different set of errors.

 $ rdmd bug.d
 libB.d(5): Error: struct libB.Opaque unknown size
 libB.d(5): Error: struct libB.Opaque no size yet for forward 
 reference
 libB.d(5): Error: struct libB.Opaque unknown size
 libB.d(5): Error: struct libB.Opaque no size yet for forward 
 reference
 libA.d(5): Error: struct libA.Opaque unknown size
 libA.d(5): Error: struct libA.Opaque no size yet for forward 
 reference

 Can someone please tell me the right way to pass an opaque 
 object between module's functions.

 --
 Thanks,
 Sarath
You have hit one of the D problems regarding extern(). The first problem is that extern(C)-declared objects inside functions are still mangled, the other problem is that extern(C) Type var; still creates var object, unlike C does (except when Type is a function pointer). The following D program is compiled and linked: extern(C) int i; void main() { i = 0; } However, a C equivalent would result in linking error due to unresolved i reference. So, what you are really doing is unusable. By the way, doing this way you are actually defining two different structs, libA.Opaque and libB.Opaque. Compiler is right in this case, because these types are incompatible, but it should print full names to show the difference explicitly. If you are working with C library, you can use following scheme: ------main.d------------ extern extern(C) struct Opaque; extern extern(C) Opaque* getObject(); void main() { Opaque* ptr = getObject(); } -----struct.c----------- struct Opaque { int i; double d; }; struct Opaque op; struct Opaque* getObject() { return &op; } ------------------------ and if you are using D code, you should use .di files.
Jan 24 2013