Clever way to avoid execution of classes functions

Hi

I'm writing a game.
I'm structuring the game is such a way that theres several "systems" (that derive from ISystem)

Ex: AssetManagementSystem, WindowSystem, etc.

and I think it would be really cool to also have another kind of systems, for debugging.
So my idea is that theres a class: DebugSystem (that derives from ISystem)

and then every system that should only work when debugging is active, derives from that. (as opposed to ISystem)

Ex: DebugLoggerSystem, DebugStatisticsSystem, etc.

My first idea was to have a bool "Debug" in the DebugSystem base class, that i could then set to true and false.

But the thing i dont like about that, is that i then manually have to check this bool in every single debug system
(Ex DebugLoggerSystem's Log function has to check as well as DebugStatisticsSystem's update function)

So I was wondering if there is some sort of clever way to make it so that: if any function of a class that derives from DebugSystem is called then it is simply ignored. (if debugging is disabled)

(So, this way i wont have to manually add code to not execute functions when debugging is disabled in every single debug system)

Thanks :)
Last edited on
Using inheritance to add orthogonal functionality is very poorly supported in languages like C++ and Java - languages which have somehow managed to kludge a semblance of object-oriented-programming atop a rigidly static type system.

Composition is a lot more flexible: strongly consider adding orthogonal functionality (like debugging support, thread safety etc.) to objects by run-time composition.

For example, here is an outline sketch (note: this is just a sketch) of this approach:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include <iostream>
#include <memory>
#include <typeinfo>
#include <mutex>

struct system_base
{
    using pointer = std::unique_ptr<system_base> ;

    virtual ~system_base() = default ;
    // rest of rule of five defaults

    virtual int foo( int arg ) const = 0 ;
    // ..
};

struct debug_proxy : system_base
{
    static pointer attach( pointer&& impl )
    {
        if(impl)
        {
            std::cout << "attach debug_proxy to object of type "
                      << typeid( *impl.get() ).name() << '\n' ;
            return std::make_unique<debug_proxy>( std::move(impl) ) ;
        }
        else return nullptr ;
    }

    explicit debug_proxy( pointer&& impl ) : impl( std::move(impl) ) {}

    private:

        virtual int foo( int arg ) const override
        {
            std::cout << "debug_proxy: call " << typeid( *impl.get() ).name()
                      << "::foo(" << arg << ")\n" ;
            try
            {
                decltype(auto) result = impl->foo(arg) ;
                std::cout << "return result: " << result << '\n' ;
                return result ;
            }
            catch( const std::exception& e )
            {
                std::cout << "*** error: " << e.what() << '\n' ;
                throw ;
            }
        }

        // ...

        pointer impl ;
};

struct body_guard : system_base
{
    static pointer attach( pointer&& impl )
    {
        if(impl)
        {
            std::cout << "attach body_guard to object of type "
                      << typeid( *impl.get() ).name() << '\n' ;
            return std::make_unique<body_guard>( std::move(impl) ) ;
        }
        else return nullptr ;
    }

    explicit body_guard( pointer&& impl ) : impl( std::move(impl) ) {}

    private:

        virtual int foo( int arg ) const override
        {
            std::cout << "body_guard: lock object " << typeid( *impl.get() ).name() << '\n' ;
            std::lock_guard<std::mutex> lock_it(lock) ;
            decltype(auto) result = impl->foo(arg) ;
            std::cout << "body_guard: unlocking object and returning result " << result << '\n' ;
            return result ;
        }

        // ...

        pointer impl ;
        mutable std::mutex lock ;
};

struct some_derived_system : system_base
{
    virtual int foo( int arg ) const override
    { std::cout << "some_derived_system::foo\n" ; return arg/2 ; }
};

int main()
{
    std::unique_ptr<system_base> p = std::make_unique<some_derived_system>() ;

    // no debug support, not thread safe
    std::cout << p->foo(23) << "\n\n" ;

    // add debugg support
    p = debug_proxy::attach( std::move(p) ) ;
    std::cout << p->foo(23) << "\n\n" ;

    // now make it thread safe
    p = body_guard::attach( std::move(p) ) ;
    std::cout << p->foo(23) << "\n\n" ;
}

http://coliru.stacked-crooked.com/a/39d24918bb2d0084
one way to do debugging is simply #defs...

#ifdef debugflag
debug code
#endif

all the code magically goes away when you compile without debug (release) and its there while you debug it.

this extends to OOP in that you can inherit or has-a the debug class which is empty in release mode and optimized away.

may not be what you are looking for.
Topic archived. No new replies allowed.