Thread overview
Structure of platform specific vs non platform specific code
May 08, 2017
Igor
May 09, 2017
Jacob Carlborg
May 09, 2017
rikki cattermole
May 09, 2017
WhatMeWorry
May 09, 2017
Stefan Koch
May 09, 2017
Igor
May 10, 2017
rikki cattermole
May 10, 2017
Jacob Carlborg
May 08, 2017
Hi,

I am following Casey Muratori's Handmade Hero and writing it in DLang. I got to Day 011: The Basics of Platform API Design where Casey explains the best way to structure platform specific vs non-platform specific code but his method cannot work in DLang since it uses modules and I am wondering what would be the best way to achieve the same in DLang.

His way is to have these files:
- platform.cpp (includes game.cpp directly, not game.h)
- game.h (declares non-platform specific data types for communicating with platform layer and both game functions that platform layer needs to call and platform functions that game needs to call)
- game.cpp (includes game.h and defines declared game functions)

This scheme makes preprocessor actually merge all files into one but logically game.* files see nothing that is platform specific.

The best idea for DLang I have is to separate platform into two modules:

- platform.d (contains only code that needs to call into game code so it imports game.d)
- platformServices.d (contains only code that game needs to call but wrapped in a common abstraction layer so game.d imports it)
May 09, 2017
On 2017-05-08 23:16, Igor wrote:
> Hi,
>
> I am following Casey Muratori's Handmade Hero and writing it in DLang. I
> got to Day 011: The Basics of Platform API Design where Casey explains
> the best way to structure platform specific vs non-platform specific
> code but his method cannot work in DLang since it uses modules and I am
> wondering what would be the best way to achieve the same in DLang.
>
> His way is to have these files:
> - platform.cpp (includes game.cpp directly, not game.h)
> - game.h (declares non-platform specific data types for communicating
> with platform layer and both game functions that platform layer needs to
> call and platform functions that game needs to call)
> - game.cpp (includes game.h and defines declared game functions)
>
> This scheme makes preprocessor actually merge all files into one but
> logically game.* files see nothing that is platform specific.
>
> The best idea for DLang I have is to separate platform into two modules:
>
> - platform.d (contains only code that needs to call into game code so it
> imports game.d)
> - platformServices.d (contains only code that game needs to call but
> wrapped in a common abstraction layer so game.d imports it)

When it comes to platform specific code, one way to do it is to use one module per platform and then one common module that publicly imports the platform specific modules:

module linux;

module windows;

module osx;

module platform;

version (linux)
    public import linux;

else version (Windows)
    public import windows;

else version (OSX)
    public import osx;

else
    static assert("unsupported platform");

Without having looked at the above mentioned guide (or whatever it is), I would say that it would make more sense that if the game module imports that platform module than the other way around.

-- 
/Jacob Carlborg
May 09, 2017
On 09/05/2017 2:53 PM, Jacob Carlborg wrote:
> On 2017-05-08 23:16, Igor wrote:
>> Hi,
>>
>> I am following Casey Muratori's Handmade Hero and writing it in DLang. I
>> got to Day 011: The Basics of Platform API Design where Casey explains
>> the best way to structure platform specific vs non-platform specific
>> code but his method cannot work in DLang since it uses modules and I am
>> wondering what would be the best way to achieve the same in DLang.
>>
>> His way is to have these files:
>> - platform.cpp (includes game.cpp directly, not game.h)
>> - game.h (declares non-platform specific data types for communicating
>> with platform layer and both game functions that platform layer needs to
>> call and platform functions that game needs to call)
>> - game.cpp (includes game.h and defines declared game functions)
>>
>> This scheme makes preprocessor actually merge all files into one but
>> logically game.* files see nothing that is platform specific.
>>
>> The best idea for DLang I have is to separate platform into two modules:
>>
>> - platform.d (contains only code that needs to call into game code so it
>> imports game.d)
>> - platformServices.d (contains only code that game needs to call but
>> wrapped in a common abstraction layer so game.d imports it)
>
> When it comes to platform specific code, one way to do it is to use one
> module per platform and then one common module that publicly imports the
> platform specific modules:
>
> module linux;
>
> module windows;
>
> module osx;
>
> module platform;
>
> version (linux)
>     public import linux;
>
> else version (Windows)
>     public import windows;
>
> else version (OSX)
>     public import osx;
>
> else
>     static assert("unsupported platform");
>
> Without having looked at the above mentioned guide (or whatever it is),
> I would say that it would make more sense that if the game module
> imports that platform module than the other way around.

Homemade hero is a video series by an experienced game dev on how to make a game from 0 to finish (still active!).
But yes this technique makes more sense for D then the original one.

May 09, 2017
On Monday, 8 May 2017 at 21:16:53 UTC, Igor wrote:
> Hi,
>
> I am following Casey Muratori's Handmade Hero and writing it in DLang.

This sounds very interesting.  Maybe make it a public github project?
May 09, 2017
On Tuesday, 9 May 2017 at 15:28:20 UTC, WhatMeWorry wrote:
> On Monday, 8 May 2017 at 21:16:53 UTC, Igor wrote:
>> Hi,
>>
>> I am following Casey Muratori's Handmade Hero and writing it in DLang.
>
> This sounds very interesting.  Maybe make it a public github project?

It can only accessible for those who bought the game.
May 09, 2017
On Tuesday, 9 May 2017 at 15:37:44 UTC, Stefan Koch wrote:
> On Tuesday, 9 May 2017 at 15:28:20 UTC, WhatMeWorry wrote:
>> On Monday, 8 May 2017 at 21:16:53 UTC, Igor wrote:
>>> Hi,
>>>
>>> I am following Casey Muratori's Handmade Hero and writing it in DLang.
>>
>> This sounds very interesting.  Maybe make it a public github project?
>
> It can only accessible for those who bought the game.

That is right. If I manage to keep it up at least a bit more I will put it at https://github.com/HandmadeHero but that is only accessible for those who buy the game.

Also thanks for the suggestions. I will definitely use it for platformServices part.

In case you are interested in the reasoning for having platform code that imports game code Casey explains that in case where you structure all platform specific code in functions that other code should call you are making a needlessly big interface polluting the API space. For example you would need CreateWindow function in such library which games would only need to call once at startup; they won't need to create and close additional windows during their execution and they don't even need to know "Window" is a thing. Also some of that code is so different on some platforms that no API can cover it clearly. For example what should one expect CreateWindow to do on Android platform.
May 10, 2017
On 09/05/2017 7:08 PM, Igor wrote:
> On Tuesday, 9 May 2017 at 15:37:44 UTC, Stefan Koch wrote:
>> On Tuesday, 9 May 2017 at 15:28:20 UTC, WhatMeWorry wrote:
>>> On Monday, 8 May 2017 at 21:16:53 UTC, Igor wrote:
>>>> Hi,
>>>>
>>>> I am following Casey Muratori's Handmade Hero and writing it in DLang.
>>>
>>> This sounds very interesting.  Maybe make it a public github project?
>>
>> It can only accessible for those who bought the game.
>
> That is right. If I manage to keep it up at least a bit more I will put
> it at https://github.com/HandmadeHero but that is only accessible for
> those who buy the game.
>
> Also thanks for the suggestions. I will definitely use it for
> platformServices part.
>
> In case you are interested in the reasoning for having platform code
> that imports game code Casey explains that in case where you structure
> all platform specific code in functions that other code should call you
> are making a needlessly big interface polluting the API space. For
> example you would need CreateWindow function in such library which games
> would only need to call once at startup; they won't need to create and
> close additional windows during their execution and they don't even need
> to know "Window" is a thing. Also some of that code is so different on
> some platforms that no API can cover it clearly. For example what should
> one expect CreateWindow to do on Android platform.

A render point[0] versus window:

[0] https://github.com/Devisualization/spew/blob/master/src/base/cf/spew/ui/rendering.d
[1] https://github.com/Devisualization/spew/blob/master/src/base/cf/spew/ui/window/defs.d#L17
May 10, 2017
On 2017-05-09 20:08, Igor wrote:

> In case you are interested in the reasoning for having platform code
> that imports game code Casey explains that in case where you structure
> all platform specific code in functions that other code should call you
> are making a needlessly big interface polluting the API space. For
> example you would need CreateWindow function in such library which games
> would only need to call once at startup; they won't need to create and
> close additional windows during their execution and they don't even need
> to know "Window" is a thing. Also some of that code is so different on
> some platforms that no API can cover it clearly. For example what should
> one expect CreateWindow to do on Android platform.

Then I suggest you have three modules: platform, game and app. The platform module doesn't need to know anything about the game module and the game module needs to know very little about the platform module. The platform module should provide some form of view object where the game can render its content. Then it won't matter if the view is a window, part of a window or window (or whatever it's called) on Android.

The app module would be responsible to bring everything together. Simple example:

module app;

import platform;
import game;

void main()
{
    auto window = CreateWindow();
    auto view = window.view;
    auto game = new Game(view);

    game.start();
}

The view would (most likely) be platform dependent but provide the same API across all platforms.

-- 
/Jacob Carlborg