Defining member fuctions of a class or struct out side of the class/struct body?

Jonathan M Davis via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Sat May 14 09:25:21 PDT 2016


On Friday, May 13, 2016 18:41:16 Jamal via Digitalmars-d-learn wrote:
> Warning D newb here.
>
> Is it possible to define a member function outside of the
> class/struct like in C++;
>
> class x { body
>      void foo(int* i);
> };
>
> void x::foo(int* i){
>      *i++;
> }
>
> Or is it just D-like to define everything inside the class/struct
> body?

The language is designed with the idea that you're going to define all of
the functions inline and not have prototypes. So, if you're trying to do it
differently, you're going to have a lot of problems.

That being said, you have two options:

1. Write a D interface file. Its extension is .di, and it's intended to be
similar to a C/C++ header file for the purpose of hiding implementation when
distributing a library without the source code. However, you still need to
write the .d file exactly like you would normally. The .di file is just for
other folks that you don't want to give the source to. So, it generates a
lot more work for you with arguably no benefit if you're just looking to
separate interface and implementation. And any templated types and
auto-return functions will still have to have their full source in the .di
file, and any functions that you want to be inlined will need their source
in the .di file. So, a lot of stuff is likely going to need to be in the .di
file anyway (especially in range-based code, since that tends to be _very_
template-heavy). To save effort in generating a .di file, you can use the
compiler to generate one for you (I think that the flags that start with -H
are for that), but last I heard, it's pretty conservative in removing stuff,
so a lot of the implementation is going to be left in there even if it
doesn't need to be. So, you're likely going to have to edit it manually. I
think that the general consensus in the D community has been that .di files
really aren't worth it. If you _need_ them, because you want to keep your
source hidden, then they're what you need, but they're a pain otherwise.

2. You can use UFCS (Universal Function Call Syntax). Ali's book talks about
it here:

http://ddili.org/ders/d.en/ufcs.html

but basically what it comes down to is that you call call a free function as
if it were a member function of its first argument. e.g.

auto foo(MyClass m, int i) {..}
auto result = myClass.foo(42);

This allows you to essentially add member functions without them having to
be member functions. You don't end up with prototypes or with them being
listed as member functions in your struct or class, but the functions are
then separate from the struct or class, and you don't need their
implementation inside the struct or class. Where UFCS is truly useful though
is it allows generic code to call functions without caring whether they're
member functions or free functions. For instance, if you use
std.algorith.find with UFCS inside of a templated function, and you call it
with UFCS, and the type that you call it on has a member function called
find which is more efficient for it than the generic find would be, then the
member function will be called, whereas for all of those types that don't
define find, the generic one will work just fine. So, your code doesn't have
to care whether the function is actually a member function or not. But a lot
of folks like to use UFCS just because it allows them to chain functions
left-to-right rather than using the classical, functional style with parens,
which chains right-to-left and ends up with a lot of parens. e.g.

auto arr = [1, 7, 19, 42, 9];
auto result = arr.filter!(a => a < 20)().map!(a => to!string(a))().array();
assert(result == ["1", "7", "19", "9"]);

or

auto arr = [1, 7, 19, 42, 9];
auto result = arr.filter!(a => a < 20)()
                 .map!(a => to!string(a))()
                 .array();
assert(result == ["1", "7", "19", "9"]);

instead of

auto arr = [1, 7, 19, 42, 9];
auto result = array(map!(a => to!string(a))(filter!(a => a < 20)(arr)));
assert(result == ["1", "7", "19", "9"]);

In any case, D is set up so that you don't normally separate declarations
and definitons like you would in C/C++, and its compilation model is _far_
more efficient than C/C++, so it's not really necessary to do it in quite
the same way that it is in C/C++. It is occasionally annoying when you just
want to see which member functions and member functions are declared on a
type, but you get used to it, and you can always generate documentation to
if you just want to see the declarations. We're in basically the same boat
as Java with this sort of thing.

- Jonathan M Davis



More information about the Digitalmars-d-learn mailing list