www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Structure of platform specific vs non platform specific code

reply Igor <stojkovic.igor gmail.com> writes:
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 08 2017
next sibling parent reply Jacob Carlborg <doob me.com> writes:
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
parent rikki cattermole <rikki cattermole.co.nz> writes:
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
prev sibling parent reply WhatMeWorry <kheaser gmail.com> writes:
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
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
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
parent reply Igor <stojkovic.igor gmail.com> writes:
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 09 2017
next sibling parent rikki cattermole <rikki cattermole.co.nz> writes:
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 09 2017
prev sibling parent Jacob Carlborg <doob me.com> writes:
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
May 10 2017