www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Incomplete types question

reply "NoUseForAName" <no spam.com> writes:
In C my favorite way of achieving encapsulation is to use 
incomplete types. The module header contains the definition of an 
incomplete type and the prototypes for the public functions which 
operate on it. I have read D has incomplete types too however D 
does not have headers and as a C guy I do not know how to 
implement this scheme without a .h/.c separation.

As I said, in C I put the incomplete declaration in the header 
while keeping the complete declaration private to the module. How 
do I do this in D? I mean type is complete within the module 
itself (and thus can be instantiated there) but incomplete for 
clients (i.e. other modules) i.e. instantiating or modifying data 
of that type directly is impossible.

P.S.: It seems D lingo for incomplete types is "opaque types".
Jul 07 2014
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/07/2014 09:44 PM, NoUseForAName wrote:
 In C my favorite way of achieving encapsulation is to use incomplete
 types. The module header contains the definition of an incomplete type
 and the prototypes for the public functions which operate on it. I have
 read D has incomplete types too however D does not have headers and as a
 C guy I do not know how to implement this scheme without a .h/.c
 separation.

 As I said, in C I put the incomplete declaration in the header while
 keeping the complete declaration private to the module. How do I do this
 in D? I mean type is complete within the module itself (and thus can be
 instantiated there) but incomplete for clients (i.e. other modules) i.e.
 instantiating or modifying data of that type directly is impossible.

 P.S.: It seems D lingo for incomplete types is "opaque types".
1) Put the opaque type and its functions into a .di file: // deneme.di module deneme; struct S; S * makeS(int i); void useS(S * s); 2) Put the implementation in its .d file: // deneme.d module deneme; import std.stdio; struct S { int i; void foo() { writefln("Executing foo() for %s", this); } } S * makeS(int i) { return new S(i); } void useS(S * s) { s.foo(); } 3) Compile the implementation as a library (just a .o in this case): $ dmd deneme.d -c (That will create the file deneme.o) 4) Write a client program that uses the interface. (Make sure that only deneme.di is visible to the client (not deneme.d).) // main.c import deneme; void main() { S * s = makeS(42); useS(s); } 5) Compile (and link) the client program and the library to make the program: $ dmd main.d deneme.o (That will create the executable 'main'.) 6) Run the program: $ ./main Executing foo() for S(42) Ali
Jul 07 2014
next sibling parent reply "NoUseForAName" <no spam.com> writes:
On Tuesday, 8 July 2014 at 06:28:51 UTC, Ali Çehreli wrote:
 On 07/07/2014 09:44 PM, NoUseForAName wrote:
 [snip]
[snip]
 3) Compile the implementation as a library (just a .o in this 
 case):
Thanks.. but this is awkward. I expected there to be keywords to declare type declarations public or private. Also using a separate .di file means I lose the whole advantage of not having to use header files. My idea here was basically to use D as a "better C", you know without the annoying stuff like header files / no real modules. Seems C is actually superior for what I want to do. .di vs. h is no real advantage, and in C I do not need to move the implementation into a library to stop other modules from using the type directly. Again, thanks for the detailed explanation but I guess I am going to stick with C for this.
Jul 08 2014
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
NoUseForAName:

 Thanks.. but this is awkward. I expected there to be keywords 
 to declare type declarations public or private.
The keywords are "public" and "private".
 I guess I am going to stick with C for this.
I suggest you to not give up yet. About 100% of the times there are equivalent or better ways to do something of C in D. Bye, bearophile
Jul 08 2014
parent reply "NoUseForAName" <no spam.com> writes:
On Tuesday, 8 July 2014 at 09:08:30 UTC, bearophile wrote:
 NoUseForAName:

 Thanks.. but this is awkward. I expected there to be keywords 
 to declare type declarations public or private.
The keywords are "public" and "private".
So I could just put.. public struct S; private struct S { int i; } .. in the .d file and that would work? I mean I expected something like this but then why would Ali post such a complex solution? Also side question. What are these .di files? D propaganda radio told me that D does not need header files because it has a real module system (like e.g. Pascal or Go) yet in the solution Ali posted these .di files seem to be.. pretty much like C header files.
Jul 08 2014
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Tuesday, 8 July 2014 at 10:47:26 UTC, NoUseForAName wrote:
 Also side question. What are these .di files? D propaganda 
 radio told me that D does not need header files because it has 
 a real module system (like e.g. Pascal or Go) yet in the 
 solution Ali posted these .di files seem to be.. pretty much 
 like C header files.
They are optional, for those situations when you don't want to distribute the entire source code (e.g. proprietary library distributed in binary form). They are usually auto-generated by the compiler (dmd -H), and I agree that it's awkward to have to define them manually.
 So I could just put..
 
 public struct S;
 
 private struct S
 {
     int i;
 }
 
 .. in the .d file and that would work? I mean I expected 
 something like this but then why would Ali post such a complex 
 solution?
I guess because it's a (the only?) way to get real incomplete types. It is the most "literal" translation of what you described. But of course, there are better ways to achieve what you actually want. Your example above doesn't work, because it would be interpreted as a redefinition of the struct. Instead, you can use a public alias to a private type: // my_module.d: private struct MyStructImpl { int x; } public alias MyStructPtr = MyStructImpl*; void doSomething(MyStructPtr foo) { ... } // my_main_program.d: MyStructPtr bar; // OK MyStructImpl foo; // Error: my_module.MyStructImpl is private
Jul 08 2014
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Marc Schütz:

 Instead, you can use a public alias to a private type:

 // my_module.d:

 private struct MyStructImpl {
     int x;
 }

 public alias MyStructPtr = MyStructImpl*;

 void doSomething(MyStructPtr foo) {
     ...
 }

 // my_main_program.d:

 MyStructPtr bar;    // OK
 MyStructImpl foo;   // Error: my_module.MyStructImpl is private
I still don't understand the point of doing this. Is disable this usable here? Bye, bearophile
Jul 08 2014
prev sibling parent reply "NoUseForAName" <no spam.com> writes:
On Tuesday, 8 July 2014 at 11:16:52 UTC, Marc Schütz wrote:
 Your example above doesn't work, because it would be 
 interpreted as a redefinition of the struct. Instead, you can 
 use a public alias to a private type:

 // my_module.d:

 private struct MyStructImpl {
     int x;
 }

 public alias MyStructPtr = MyStructImpl*;

 void doSomething(MyStructPtr foo) {
     ...
 }

 // my_main_program.d:

 MyStructPtr bar;    // OK
 MyStructImpl foo;   // Error: my_module.MyStructImpl is private
Thanks, that is what I was looking for.
Jul 08 2014
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
NoUseForAName:

 Thanks, that is what I was looking for.
Why do you need a public pointer type but private struct type? Bye, bearophile
Jul 08 2014
parent "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Tuesday, 8 July 2014 at 11:55:01 UTC, bearophile wrote:
 NoUseForAName:

 Thanks, that is what I was looking for.
Why do you need a public pointer type but private struct type?
As the OP wrote:
 In C my favorite way of achieving encapsulation is to use 
 incomplete types.
(Also known as PIMPL.) In C, it's the only type-safe way to get encapsulation, AFAIK. In languages with access protection, it's probably not as useful, but I'm sure it has its applications...
Jul 08 2014
prev sibling parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Tuesday, 8 July 2014 at 11:42:54 UTC, NoUseForAName wrote:
 On Tuesday, 8 July 2014 at 11:16:52 UTC, Marc Schütz wrote:
 Your example above doesn't work, because it would be 
 interpreted as a redefinition of the struct. Instead, you can 
 use a public alias to a private type:

 // my_module.d:

 private struct MyStructImpl {
    int x;
 }

 public alias MyStructPtr = MyStructImpl*;

 void doSomething(MyStructPtr foo) {
    ...
 }

 // my_main_program.d:

 MyStructPtr bar;    // OK
 MyStructImpl foo;   // Error: my_module.MyStructImpl is private
Thanks, that is what I was looking for.
Addendum: You also need to protect the members of MyStructImpl, like so: private struct MyStructImpl { private: // or `package:` int x; } Otherwise, they would still be accessible via dereferencing. (Don't worry, you _can_ access private members inside the module the struct is declared in, as access protection in D only applies across module boundaries.)
Jul 08 2014
parent "NoUseForAName" <no spam.com> writes:
On Tuesday, 8 July 2014 at 12:04:40 UTC, Marc Schütz wrote:
 Addendum: You also need to protect the members of MyStructImpl, 
 like so:

 private struct MyStructImpl {
 private:  // or `package:`
     int x;
 }

 Otherwise, they would still be accessible via dereferencing.
Thanks again.
Jul 08 2014
prev sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Ali Çehreli:

 1) Put the opaque type and its functions into a .di file:
[...] It seems your answer has scared NoUseForAName off. Next times we need to be more careful, and suggest a D-idiomatic solution instead. Bye, bearophile
Jul 08 2014
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/08/2014 03:35 AM, bearophile wrote:

 Ali Çehreli:

 1) Put the opaque type and its functions into a .di file:
[...] It seems your answer has scared NoUseForAName off. Next times we need to be more careful, and suggest a D-idiomatic solution instead. Bye, bearophile
I should have first asked what the need was. Ali
Jul 08 2014
prev sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
NoUseForAName:

 In C my favorite way of achieving encapsulation is to use 
 incomplete types. The module header contains the definition of 
 an incomplete type and the prototypes for the public functions 
 which operate on it.
Probably in D you can archive something similar with different means. Bye, bearophile
Jul 07 2014