Jump to page: 1 2
Thread overview
Incomplete types question
Jul 08, 2014
NoUseForAName
Jul 08, 2014
Ali Çehreli
Jul 08, 2014
NoUseForAName
Jul 08, 2014
bearophile
Jul 08, 2014
NoUseForAName
Jul 08, 2014
Marc Schütz
Jul 08, 2014
bearophile
Jul 08, 2014
NoUseForAName
Jul 08, 2014
bearophile
Jul 08, 2014
Marc Schütz
Jul 08, 2014
Marc Schütz
Jul 08, 2014
NoUseForAName
Jul 08, 2014
bearophile
Jul 08, 2014
Ali Çehreli
Jul 08, 2014
bearophile
July 08, 2014
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".
July 08, 2014
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

July 08, 2014
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
July 08, 2014
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.
July 08, 2014
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
July 08, 2014
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
July 08, 2014
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.
July 08, 2014
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
July 08, 2014
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
July 08, 2014
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.
« First   ‹ Prev
1 2