Skip to main content
GameDev.net gamedev.net
🔒 Locked

FPL - a single header file platform abstraction library

Started by Finalspace Aug 2, 2017 at 11:14 AM 36 replies 11k views
Original Post
Finalspace
Finalspace

Hi,

i wanted to present you my recent project i am working on which is a platform abstraction library.
This library is designed to be simple, non-bloated, can be included however you want and do not require any thirdparty libraries at all. It will simply abstract away all relevant platform specific things in a simple to use api without hiding any data. The main focus is game/media/simulation development, so you get a window, audio playback and a rendering context.


Why:

- Moving away from object orienation to handmade programming
- Most platform abstraction libraries are bloated, have a bad api, cannot be debugged, are slow, has bad memory management
- I dont want to write platform specific code anymore, just once for each platform with a fixed set of features and thats it.
- Learning C99 low level programming


Mindset:

- Its written in C99 but is C++ compatible
- There is just a single header file you have to include and thats it.
- C-Runtime support can be disabled
- The code style is simple and clean
- Its full open source, so you can use and extend it however you want
- Uses just the built-in operating system functions/libraries
- Works well with other libraries as well (ImGUI, Box2D, Glew, STL, etc.)
- Some features can be compiled out as needed (Window, OpenGL, etc.)
- Fixed and small memory footprint
- Easy to use api
- Can be debugged


Features:

- Window creation and handling
- OpenGL legacy and modern rendering context creation
- Memory allocation and deallocation
- Timing operations
- Threading and syncronisation
- File/Path IO
- String conversion functions
- Atomic operations
- Dynamic library loading
- Console out
- Gamepad input
- Audio playback


History:

Initially the project was fully written with 100% C/89 compability in mind without needing the c-runtime library, but as i see myself always fighting against C or missing any features like console output, i decided to move to a minimal C++ version.

After a while i upgraded to C++/11, but felt there was no need for C++ at all...

Now its written in C99 and it was the best choice ever.


Current state:

- Win32 support is complete
- Linux support is almost complete


Planned:

- More video drivers (Direct2D/3D/Vulcan)
- More audio drivers (XAudio, Wasapi)
- More platforms (MacOSX, maybe Android not sure)
- More docs & tutorials
- More tests


Source:

https://github.com/f1nalspace/final_game_tech


Feedback/Comments:

I would like to know what you think about, what is missing, how can i improve it, what do you want from it, etc.


Greetings,
Final

TheComet
TheComet

I skimmed your code and there are two (maybe three) things that stuck out to me.


#define FPL_NULL 0

This is not how you should be defining NULL. Either use (void*)0 or nullptr if C++11 is available.


#if !FPL_ENABLE_C_RUNTIME_LIBRARY
void __stdcall WinMainCRTStartup() {
    int result = wWinMain(GetModuleHandleW(0), 0, 0, 0);
    ExitProcess(result);
}
#endif // !FPL_ENABLE_C_RUNTIME_LIBRARY

I read your comment further up the file about how you hate the CRT, but this is clearly taking it too far. I'll quote a section of ExitProcess()'s documentation:

Quote

Calling ExitProcess in a DLL can lead to unexpected application or system errors. Be sure to call ExitProcess from a DLL only if you know which applications or system components will load the DLL and that it is safe to call ExitProcess in this context.

And that's pretty much the point at which I stopped reading your code. Parts of it looked good, other parts looked really, really bad. A more important question to me is: Who needs this? Why does this exist? All of this stuff is already available in other libraries or game frameworks.

As a side note, why are people writing more and more of these "header only" libraries? The preprocessor seems like a flimsy way to configure how your code should compile. A more solid solution is to use a proper build system.

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty
Finalspace
Finalspace
51 minutes ago, TheComet said:

I skimmed your code and there are two (maybe three) things that stuck out to me.



#define FPL_NULL 0

This is not how you should be defining NULL. Either use (void*)0 or nullptr if C++11 is available.



#if !FPL_ENABLE_C_RUNTIME_LIBRARY
void __stdcall WinMainCRTStartup() {
    int result = wWinMain(GetModuleHandleW(0), 0, 0, 0);
    ExitProcess(result);
}
#endif // !FPL_ENABLE_C_RUNTIME_LIBRARY

I read your comment further up the file about how you hate the CRT, but this is clearly taking it too far. I'll quote a section of ExitProcess()'s documentation:

And that's pretty much the point at which I stopped reading your code. Parts of it looked good, other parts looked really, really bad. A more important question to me is: Who needs this? Why does this exist? All of this stuff is already available in other libraries or game frameworks.

As a side note, why are people writing more and more of these "header only" libraries? The preprocessor seems like a flimsy way to configure how your code should compile. A more solid solution is to use a proper build system.

I tried FPL_NULL (void *)0 - but i had trouble with it, so i simply set it to zero. But yes i agree, using nullptr on C++/11 would be better, but how do i detect if C++/11 is available - through preprocessor directives? Btw. FPL_NULL is internal only, so its not meant to be used outside.


That entire no CRT thing - i am not sure about this. Its a experiment to see if it works, because some people dont want to include even the C-Runtime Library at all. Also regards DLL´s and ExitProcess and need to think it more, as a matter of fact i havent created a library project yet - but this will come soon. When i see that no-CRT is support is too complicated, i will drop it. Right know i dont see any benefit disabling CRT, except for smalling out the executable size.


Why header library only? Because its simple and works all the time. No linking madness, no dll´s needed - besides from the standard system operating stuff which is included anyway.


Regards build-systems: I dont like build-systems at all, all i ever used was just awful: Slow, hard to configure, not working -> On any programming language the same bad experience.

But what i will do for sure, is making several simple build scripts (Batch, Shell) which just builds the samples in the simplest possible way for a set of compilers/platforms.

_Silence_
_Silence_
52 minutes ago, Finalspace said:

C++/11 would be better, but how do i detect if C++/11 is available - through preprocessor directives?

You have it in your own code:



	#ifdef __cplusplus
	


ongamex92
ongamex92
Quote

That entire no CRT thing - i am not sure about this. Its a experiment to see if it works, because some people dont want to include even the C-Runtime Library at all.

I do not know a person or a product that does not link with CRT. Starting form small games, 3D DCCs, even some "serious software" uses it.

Quote

Why header library only? Because its simple and works all the time. No linking madness, no dll´s needed - besides from the standard system operating stuff which is included anyway.

A header and a *.cpp is equally simple to me.


#define fpl_globalvar static
#define fpl_inline inline
#define fpl_internal static
#if FPL_API_AS_PRIVATE
#    define fpl_api static
#else
#    define fpl_api extern

This is unnecessary code to me, it doesn't tell much to the reader and these have no functional benefit, actually it is wrong:
if I "fpl_globalvar int x;" in a *.cpp this will actually make the variable "private" to that *.cpp, making it inaccessible to other files.

Finalspace
Finalspace
40 minutes ago, ongamex92 said:

I do not know a person or a product that does not link with CRT. Starting form small games, 3D DCCs, even some "serious software" uses it.

A header and a *.cpp is equally simple to me.



#define fpl_globalvar static
#define fpl_inline inline
#define fpl_internal static
#if FPL_API_AS_PRIVATE
#    define fpl_api static
#else
#    define fpl_api extern

This is unnecessary code to me, it doesn't tell much to the reader and these have no functional benefit, actually it is wrong:
if I "fpl_globalvar int x;" in a *.cpp this will actually make the variable "private" to that *.cpp, making it inaccessible to other files.

Insomniac Games and EA for example do not use the C standard library, they even dont use the STL.

Also there are others people in the industry who do not use the C standard library as well.

For myself, i dont care but its great when you have the option to not need to ship the C Runtime with your game (VC++ redist for example).


Static has more than one meaning, private scope for this file only, local persisting state, global variable, class field in c++, etc. Even though it just mean: Private in this file only. Techniqually there are no differences between all of them, but this way you see right away which scope its meant to be in.


Right, there are no difference between header and source files. C++ has no definition of a file, you just have translation units which gets compiled to object files - thats it. I dont want to force users to have a separate translation unit, just for the library - if they want, they can have one but its not required.

TheComet
TheComet


1 hour ago, Finalspace said:

how do i detect if C++/11 is available - through preprocessor directives?

https://stackoverflow.com/a/29407260/4383258


#if defined(__cplusplus) && __cplusplus >= 201103L
#define FPL_NULL nullptr
#endif


"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty
Shaarigan
Shaarigan

I will definitely have a look at it but as most other people might, I have those platform specific stuff already build into my game engine from the beginning. Platform detection for PC and propably Console, Threads, Locking, Atomics, Extensions (via LoadLibrary and similar) and even a Window class as same as some graphics API dependent startup code

Finalspace
Finalspace

New version:


- Dropped C support

- Dropped No-C-Runtime support

- Removed all FPL prefixes from functions and types

- Moved to a more c++ ish api (Namespaces, const reference, enum class, constexpr, nullptr)

- Nested namespaces to categorize certain things (window, memory, strings, atomics, threading, etc.)

Finalspace
Finalspace
On 6.10.2017 at 9:04 PM, ferreiradaselva said:

You need a reference doc

If you add something like this https://github.com/ferreiradaselva/cgdfw/blob/master/REFERENCE.md it will make things much easier.

Thanks i will do that - good idea!

Is there a way to export all my api structs and functions into that automatically?

Never mind, i will write a command line tool which does that.

Finalspace
Finalspace

Monster Update:

  • Added memory::CopyMemory
  • Added fpl::GetLastError and fpl::GetLastErrorCount for proper error handling
  • Added files::CreateBinaryFile and files::OpenBinaryFile for wide file paths
  • Added basic support for creating a modern opengl rendering context, see VideoCompabilityProfile in VideoSettings
  • Added support for enabling opengl vsync through WGL
  • Returns char * for all paths:: get like functions
  • Returns char/wchar_t * for all strings:: functions
  • Fixed files::CreateBinaryFile was never able to overwrite the file.
  • Fixed #include was in some namespaces defined
  • Fixed files::ClearMemory was wrong
  • Replaced all const constants with constexpr
  • Removed template code / Replaced it with macros
Finalspace
Finalspace

Update:

  • All types/structs/fields/functions documented
  • [Win32] Fixed legacy opengl (GL_INVALID_OPERATION)
  • [Win32] Support for WGL opengl profile selection


Todo:

  • Create full reference.md documentation / upload doxygen documentation.
  • Win32 audio output + api
  • Win32 threading + api
  • Win32 clipboard get/set
Finalspace
Finalspace

Update:

  • Renamed: All memory/library/threading functions
  • Renamed: CopyFile/DeleteFile renamed (Stupid win32!)
  • Renamed: All internal opengl defines renamed, so that it wont conflict with other libraries
  • Fixed: [Win32] strings::All Wide conversions was not working properly
  • Removed: [Win32] All undefs removed
  • Changed: [Win32/OpenGL] Check for already included gl.h
  • Added: Basic threading creation and handling

Todo:

  • Create full reference.md documentation / upload doxygen documentation.
  • Win32 audio output + api
  • Win32 mutex / condition + api
  • Win32 clipboard get/set
  • Linux platform support
Finalspace
Finalspace

Update v0.4.0 alpha:

  • Changed: All FPL_ENABLE_ defines are internal now, the caller must use FPL_NO_ or FPL_YES_ respectivily.
  • Changed: AtomicCompareExchange is now AtomicCompareAndExchange
  • Changed: InitFlags::VideoOpenGL is now InitFlags::Video
  • Added: Software rendering support
  • Added: VideoDriverType enumeration for selecting the active video driver
  • Added: video::GetVideoBackBuffer with [Win32] implementation
  • Added: video::ResizeVideoBackBuffer with [Win32] implementation
  • Added: FPL_PETABYTES macro
  • Added: FPL_EXABYTES macro
  • Added: FPL_ZETTABYTES macro
  • Added: FPL_YOTTABYTES macro
  • Added: FPL_MIN macro
  • Added: FPL_MAX macro
  • Added: MutexCreate with [Win32] implementation
  • Added: MutexDestroy with [Win32] implementation
  • Added: MutexLock with [Win32] implementation
  • Added: MutexUnlock with [Win32] implementation
  • Added: SignalCreate with [Win32] implementation
  • Added: SignalDestroy with [Win32] implementation
  • Added: SignalWait with [Win32] implementation
  • Added: SignalWakeUp with [Win32] implementation
  • Added: GetClipboardAnsiText with [Win32] implementation
  • Added: GetClipboardWideText with [Win32] implementation
  • Added: SetClipboardText with [Win32] implementation for ansi and wide strings
  • Added [MSVC]: AtomicExchangeS32 (Signed integer)
  • Added [MSVC]: AtomicExchangeS64 (Signed integer)
  • Added [MSVC]: AtomicAddS32 (Signed integer)
  • Added [MSVC]: AtomicAddS64 (Signed integer)
  • Added [MSVC]: AtomicCompareExchangeS32 (Signed integer)
  • Added [MSVC]: AtomicCompareExchangeS64 (Signed integer)
  • Fixed [MSVC]: AtomicExchangeU32 was not using unsigned intrinsic
  • Fixed [MSVC]: AtomicExchangeU64 was not using unsigned intrinsic
  • Fixed [MSVC]: AtomicAddU32 was not using unsigned intrinsic
  • Fixed [MSVC]: AtomicAddU64 was not using unsigned intrinsic
  • Fixed [MSVC]: AtomicCompareExchangeU32 was not using unsigned intrinsic
  • Fixed [MSVC]: AtomicCompareExchangeU64 was not using unsigned intrinsic
  • Implemented [Win32]: GetProcessorCoreCount
  • Implemented [Win32]: Main thread infos
  • Performance [Win32]: GetProcessorName (3 loop iterations at max)

State:

Its almost feature complete for Win32, only audio output is missing.

Next i will clean up all the ifdef madness to get a more clean code structure - so i have a single ifdef block for each platform/backend. Also i am thinking about removing the sub namespaces, just because its so much useless typing for no reason.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.