std.experimental.allocator and @nogc

ZombineDev via Digitalmars-d digitalmars-d at puremagic.com
Mon May 9 05:33:51 PDT 2016


On Monday, 9 May 2016 at 12:02:19 UTC, rikki cattermole wrote:
> On 09/05/2016 11:56 PM, ZombineDev wrote:
>> On Monday, 9 May 2016 at 11:20:00 UTC, rikki cattermole wrote:
>>> On 09/05/2016 11:12 PM, ZombineDev wrote:
>>>> On Monday, 9 May 2016 at 10:33:27 UTC, rikki cattermole 
>>>> wrote:
>>>>> I've done windowing and image libraries before.
>>>>> You are correct, you do not need OOP.
>>>>>
>>>>> But if you want to keep implementation nicely separated out 
>>>>> from
>>>>> usage, you really do. Which is a major part of my 
>>>>> requirements.
>>>>
>>>> Well, in my example above, everything is nicely separated, 
>>>> easy to use,
>>>> yet quite flexible. And still there is no Java-style OOP. Do 
>>>> you have
>>>> any examples, where the use of classic OOP would provide a 
>>>> strictly
>>>> superior design?
>>>
>>> You're using templates. While this might be ok for image 
>>> library.
>>> I cannot use this for the windowing library. The 
>>> implementation may
>>> not be known and must be plugable at runtime.
>>> The reality is, just because you say you know about something 
>>> at
>>> compile time doesn't mean the system that runs a program does.
>>
>> I'm still not convinced. You have a fixed at CT number of
>> implementations. Even if you don't know at CT the actual 
>> platform that
>> will be used, you can still choose at RT the correct template. 
>> Example:
>
> You do not.
> Shared libraries remember them?

You will statically link the windowing library and it will 
dynamically load the necessary shared/dynamic libaries of the 
platform at runtime. What's the problem? E.g. Derelict* does the 
same.

>> // platform name can be "X11", "mir", "Wayland", etc.
>> auto getPlatformWindow(CTArgs...)(string platformNameRTArg)
>> {
>>     switch (platformNameRTArg.toLower)
>>     {
>>         default:
>>              enforce(false, "Unknown platform: " ~ 
>> platformNameRTArg);
>>
>>         foreach (platform; EnumMembers!Platform)
>>             case name.stringof:
>>                  // Use std.experiment.typecons.Wrap if you 
>> need a
>> common type.
>>                  return getWindow!(name, CTArgs);
>>     }
>>     assert (0);
>> }
>
> Platform is undefined, did you mean IPlatform?

Platform obviously an enum. The return type can be a plain 
struct, a Variant, or a std.experimental.typecons.wrap, depending 
on your needs.

>> But that's besides the point. Even with OOP, your return type 
>> can be an
>> interface that has template parameters like allocators, 
>> policies, etc.
>> You're providing some the arguments at CT and the rest of the 
>> return
>> type is dynamically polymorphic. E.g.:
>>
>> interface Window(Allocator, Policy);
>> Window!(Allocator, Policy) getWindow(alias allocator, 
>> Policy)(RunTimeArgs);
>
> Sure you can, but now you have added another layer of 
> indirection between implementation and usage because you can't 
> use templated types in between.

There's no indirection (besides the interface, which would also 
be the case with your approach) and you **should** use templated 
types :)

>> My point is that there's no need for IAllocator, even if you 
>> want to use
>> OOP so badly.
>> But in most cases you will either write different code for 
>> different
>> platforms, making interfaces unnecessary, or you would be able 
>> to hide
>> the differences behind a struct.
>>
>> And when the allocator and the other policies are template 
>> parameters
>> you will know at CT that your code is @nogc.
>
> Again, no templates. You cannot initialize them at runtime, and 
> since you won't know all implementations until it executes, 
> well, you've got a problem.
> Its a many to many problem. I faced this with Cmsed as well.

As I demonstrated, it's not a problem. You just need to choose 
the template at runtime. You have a fixed number of 
implementations which are all statically linked. Only the one 
chosen at runtime will be initialized and at the moment of 
initialization it can do the necessary dynamic loading.

In the example above:

>> interface Window(Allocator, Policy);
>> Window!(Allocator, Policy) getWindow(alias allocator, 
>> Policy)(RunTimeArgs);

Since you don't need to switch Allocator implementations at 
run-time (and if you switch them you will most certainly get 
silent memory corruption), you can leverage the fact that some 
stuff are known at CT and leave the rest to RT.


More information about the Digitalmars-d mailing list