Defining function prototypes in a header file without an implemented body

Hey all,
I didn't post this in the beginners section as I think it is abit advanced for it, but here is my problem:

Basically, I have made a program which implements the platform specific layers (such as entry function, file loading, timing functions etc.) that gets compiled into a .exe (or platform equivalent).

But I want to make this portable and reusable across other projects, so the entry function for the platform will call the function "AppMain" which is the generic main function that is not reliant on the underlying platform etc. (i.e defined in a .h file that the project module will implement).

Ideally I would want to build the AppMain code into its own library. However, This AppMain code would want access to the Platform functions such as the functions compiled into the .exe.

This has confused me somewhat and has forced me to build both the AppMain module and the Platform Code into the same exe file so they can use each others functions.

Is there any way I can create a header file (with all the function prototypes in) but they do not get implemented in the Platform code but rather they can be 'guaranteed' to be available at runtime?

Sorry if this sounds confusing (as I am quite confused by it myself) but here is what I am trying to achieve in a high level view:

win32layer.cpp: (implements all the functions defined in Platform.h)
1
2
3
4
5
6
7
#include <AppMain.h>

    int main(int argc, char** argv)
    {
        //call AppMain
        return AppMain(argc, argv);
    }


AppMain.h: (defines the functions that each new project has to implement)
int AppMain(int argc, char** argv);

AppMain.cpp:
1
2
3
4
5
6
7
8
9
#include <AppMain.h>
    #include <Platform.h>

    int AppMain(int argc, char** argv)
    {
        //print hello world to the platform output
        Platform_Log("Hello, World\n");
        return 0;
    }



in this scenario of course I could not compile the platform functions as the application has not been created and thus appmain cannot call the platform functions because that has not been created etc....

Anyone know of any way to overcome this?

Thanks,
Nick
It seems like you're looking at it backwards. Normally you'd put the platform-independent code in the main executable and the platform-dependent code in separate libraries/optional files.
> Is there any way I can create a header file (with all the function prototypes in)
> but they do not get implemented in the Platform code
> but rather they can be 'guaranteed' to be available at runtime?

This is a canonical use-case for the singleton pattern, isn't it?

(A pattern that is an unfortunate victim of programmer incompetence. Newbies tend to overuse - abuse - it. And newbies who think that they are not newbies advice newbies who know that they are newbies never to use it.)

platform.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//////////////  header platform.h /////////////
// include guard
struct platform // singleton
{
    static platform& get() ;

    virtual ~platform() = default ;

    // public interface
    int main( /* ... */ ) ;
    void foo( /* ... */ ) ;
    void bar( /* ... */ ) ;
    // ...

    private:
        // functions to be implemented for each platform
        virtual int main_impl( /* ... */ ) = 0 ;
        virtual void foo_impl( /* ... */ ) = 0 ;
        virtual void bar_impl( /* ... */ ) = 0 ;
        // ...

        platform( const platform& ) = delete ;
        platform( platform&& ) = delete ;
        platform& operator= ( const platform& ) = delete ;
        platform& operator= ( platform&& ) = delete ;

    protected: platform() ;
};


platform.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//////////////  implementation platform.cc /////////////

#include "platform.h"
#include <stdexcept>

namespace
{
    platform* the_platform = nullptr ; // pointer to singleton object
}

platform::platform()
{
    if(the_platform) throw std::logic_error( "second instance of platform" ) ;
    the_platform = this ;
    // note: if concurrency is an issue, check and assignment should be wrapped
}

platform& platform::get()
{
    if( !the_platform ) throw std::logic_error( "singleton instance of platform not instantiated" ) ;
    return *the_platform ;
}

int platform::main( /* ... */ ) { return main_impl( /* ... */ ) ; }
void platform::foo( /* ... */ ) { foo_impl( /* ... */ ) ; }
void platform::bar( /* ... */ ) { bar_impl( /* ... */ ) ; }
// ... 


app_main.h
1
2
3
4
5
6
//////////  header app_main.h ///////////////
// include guard
#include "platform.h"

int app_main( /* ... */ ) ;
// ... 


app_main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//////////  implementation app_main.cc ///////////////////////
//#include "app_main.h"

int app_main( /* ... */ )
{
    platform& this_platform = platform::get() ;

    this_platform.foo( /* ... */ ) ;

    int result = this_platform.main( /* ... */ ) ;

    this_platform.bar( /* ... */ ) ;

    // etc.

    return result ;
}
// ... 


main.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
////////// main.cc /////////////////////////
#include "app_main.h"
#include <iostream>

int main()
{
    try
    {
        return app_main() ;
    }
    catch( const std::exception& e )
    {
        std::cerr << "platform is not instantiated\n" ;
    }
}


free_bsd.cc (specific platform implementation)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
////////////////// freebsd_main.cc ///////////
/////////////////  platform specific ////////////
#include "platform.h"
#include <iostream>

namespace
{
    struct free_bsd : platform // singleton implementation for free_bsd platform
    {
        private:
            // functions to be implemented for each platform
            virtual int main_impl( /* ... */ ) override { std::cout << "free_bsd::main()\n" ; return 0 ; }
            virtual void foo_impl( /* ... */ ) override { std::cout << "free_bsd::foo()\n" ; }
            virtual void bar_impl( /* ... */ ) override { std::cout << "free_bsd::bar()\n" ; }

            // ...
    } singleton ; // instantiate singleton object
}
    // note: in certain scenarios, may need to take care of order of initialisation of statics 

http://coliru.stacked-crooked.com/a/ec6c8fd3bfdda7d4
It seems like you're looking at it backwards. Normally you'd put the platform-independent code in the main executable and the platform-dependent code in separate libraries/optional files.


I don't see how that would make sense? For example if I were to make a mini-game engine surely the code that implements the platform specific functions (i.e the int main () ) would be built out in the executable and the engine (i.e the module that implements the app_main() or for a more on-topic example - the 'engine_init()' function) would be referenced as a .lib or its equivalent?

This is a canonical use-case for the singleton pattern, isn't it?


That is exactly what I was thinking, and like you say... it is frowned upon. But as far as my knowledge goes - I can't really see another way.

Although, I was thinking about how you have to use the wgl extensions to expose the OpenGL2+ functions on Windows. Isn't that basically saying "This is the function I want to use and its guaranteed to be there" (in some crude form....). But I suppose that is just using function pointers though, right?
> But I suppose that is just using function pointers though, right?

Yes. The implementation of run-time polymorphism, in the end, is just a trick using indirection.

The initialisation the table of function pointers, providing a mechanism for going from object to the v-table, the run-time lookup, and the call with the retrieved function pointer passing this - wrapped in a thick layer of syntactic sugar - is what support for object-oriented programming by assorted languages really is.
I don't see how that would make sense? For example if I were to make a mini-game engine surely the code that implements the platform specific functions (i.e the int main () ) would be built out in the executable and the engine (i.e the module that implements the app_main() or for a more on-topic example - the 'engine_init()' function) would be referenced as a .lib or its equivalent?
That would imply that in your design, the fact that a game engine is implemented is incidental, and what matters is that the program is for that particular platform. Which I guess would make sense if you intend to swap out the engine during testing or something, but that's rather unusual.
Usually, the central part is the implementation of your engine, and the surrounding system and how to interact with it is incidental. So, if any part gets moved to a library, that would be it.
@JLBorges: So would you suggest going with the singleton approach to this problem?

@helios: Could you expand on what you are saying? I'm quite confused :( - sorry!
> So would you suggest going with the singleton approach to this problem?

Yes.

I most emphatically do not subscribe to the view that the singleton pattern should never be used. I see no cogent reason why it should not be used here. And I see a lot of reasons favouring its use in this scenario.


For the record, I strongly prefer a design where the game engine is in one or more libraries. With the platform specific code in another library. Language bindings in other libraries. And so on.
What I mean is this: suppose you're building a sort of modular house. You can either build the house fixed in place so that you can take the door out and replace it with another, or you can build the door fixed in place so that you can replace the house. Now, both are valid design choices and will let you accomplish more or less the same things, but one is definitely weirder than the other.
@JLBorges: Great, thanks I'll give it a shot on the weekend :)

@helios: I see what you mean now, and a great way of describing it to!

Thanks Guys :)
Topic archived. No new replies allowed.