Sign in to follow this  

Unity concept of a new engine

This topic is 4105 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello, Community I'm just soliciting your advice on how I should be writing my engine. Right now, I have all my debugging utilities in separate classes, ie LogFile, Exception, Assert, MemoryManager, etc. But I was just wondering if it is better to take advantage of inheritance and create one main class like Object and stick all of this stuff into it, and have every class inherit from it. Would there be an immediate slowdown because of it? Thanks for any advice[smile], exorcist_bob

Share this post


Link to post
Share on other sites
This COM style 'inherit absolutely everything' mentality is great(ish) for many applications, but over-OOPing your code is generally a bad idea for games programming. There are arguments for and against it, but you'll probably find that most people in this forum prefer to have global objects rather than a single global instance of a class to encapsulate base functionality.

Unless your engine is special somehow and will benefit greatly from the 'IUnknown' setup, I recommend you concentrate your efforts on making it work, rather than making it abstract 'nicely'. Game engines, unlike many business applications, tend not to have several tiers of classes that are all subtly but significantly different: Your AI agent will be radically different from your mesh object, which, in turn, will be radically different from the network socket and camera class. When the core components of your engine are so diverse, there is little to be gained and much deadwood to carry around using the proposed model.

Regards
Admiral

Share this post


Link to post
Share on other sites
I just thought that something like this might be useful:


class Object
{
protected:
virtual void Dump() = 0;
}

class Something: public Object
{
private:
//Have to override this, since it is pure virtual
void Dump()
{
//Dump all the variables in this class
}
};



Since Object would be common to all classes, if I threw an exception, all dumps of all active instances of all classes inherited by Object would execute their Dump() routines. Or isn't this feasible?[sad]

Thanks,
exorcist_bob

Share this post


Link to post
Share on other sites
It's feasible, but not all that useful.
The fact that each class that derives from Object would have to override Dump() in their own unique way means that you aren't saving yourself much effort at all. A more popular base functionality to put in an 'object' class is reference counting, which has its advocates, but is also unnecessary, in this programmer's opinion.

If you really want to use inheritance in your engine, nobody is stopping you, and it probably won't cause you any problems - maybe it'll even solve some. It's just that most wouldn't consider the benefits to outweight the costs.

Regards
Admiral

Share this post


Link to post
Share on other sites
Thanks for your replies, Admiral!

Would this cause a significant slowdown? I have heard both sides of the story. Some say that inheritance is slow and should be avoided, but I have also heard that the slowdown is barely significant.

Thanks,
exorcist_bob

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
You're over complicating your logging. Just use global functions, not everything needs to be a class. Otherwise (bet you're using a horrid and evil singleton for it...) you're doing a lot of extra typing for nothing.

Example:
Log::Get().Write(...);

or..
Log(...);

You pick which would be easier and more likely to be used? ;-)

Remember, even if its just for you, KISS.

Share this post


Link to post
Share on other sites
Quote:
Original post by exorcist_bob
Would this cause a significant slowdown? I have heard both sides of the story. Some say that inheritance is slow and should be avoided, but I have also heard that the slowdown is barely significant.


There is absolutely no slowdown with inheritance. The only thing that introduces a slight slowdown are virtual members.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
You're over complicating your logging. Just use global functions, not everything needs to be a class. Otherwise (bet you're using a horrid and evil singleton for it...) you're doing a lot of extra typing for nothing.

Example:
Log::Get().Write(...);

or..
Log(...);

You pick which would be easier and more likely to be used? ;-)

Remember, even if its just for you, KISS.


But it is more that just that. If I can get this to work, if something goes wrong in one part of the engine, it forces all instances of classes to dump their var states. This base class would include logging, exception throwing, assertions, and memory management. This way, I think I would be able to dump a lot of useful information.

PS. I'm not using a singleton. In fact, there isn't a single global variable or function in the engine yet. As for your example, couldn't you use just a #define, if you didn't want to type all that?

Thanks for your help,
exorcist_bob

Share this post


Link to post
Share on other sites
How would inheriting from a common base class help your design? As I see it, you'd make your design worse because you completely ignore separation of concerns and information hiding. The "dumping" you talk about shouldn't force all classes to dump their contents, instead you should have a base class called dumpable or something like that, so that the functionality of dumping is seperate and optional. I assume you want to make a logging a part of the common base class because you need an easy way to access the logging facilities, in that case a global, monostate or singleton would be just as good (but not optimal). As I see it there is absolutely no reason to make logging a part of the common base class. The common base class shouldn't depend on assertions or memory management either, the implementation of classes could depend on the assertion and memory management system, but don't force this unnecessary dependency onto all of your classes.

Could you please tell us exactly why you believe this new design is better? Inheritance doesn't just make your program more object oriented if that's what you believe.

Share this post


Link to post
Share on other sites
With EVERYONE here opposing me, I doubt this will make any difference.

It is my personal belief that it is hard to track where something went wrong, and causes your engine to crash, since a lot of times, you don't run a full-fledged game in the vc++ debugger. Since everything that handles a given set of data has the potential to mess it up, as an engine gets larger, it becomes harder and harder to track something down, and soon, you forget where it going and has gone. By forcing everything to dump data to a file, I believe it will be easier to track down and eliminate it. I think that the easiest way to do this is with a base class that all classes must inherit from. Since I can't call a pure virtual function in a base class, I have to call it from all classes that inherit from it.


// test_inheritance.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
using namespace std;

#define DumpClass() if(m_bMustDump) Dump()

class base
{
public:
base()
{

};

~base()
{

};
void Exception()
{
m_bMustDump = true;
};

protected:
virtual void Dump() = 0;

static bool m_bMustDump;
};

class derived1: public base
{
public:
~derived1()
{
DumpClass();
};
private:
void Dump()
{
cout << "derived1" << endl;
};
};

class derived2: public base
{
public:
~derived2()
{
DumpClass();
};
private:
virtual void Dump()
{
cout << "derived2" << endl;
};
};

class derived3: public derived2
{
public:
~derived3()
{
DumpClass();
};
private:
void Dump()
{
cout << "derived3" << endl;
};
};

bool base::m_bMustDump = false;

int _tmain(int argc, _TCHAR* argv[])
{
{
derived1 d1;
derived2 d2;
derived3 d3;

d1.Exception();
};

char ch;
cin >> ch;

return 0;
}



Thanks,
exorcist_bob

Share this post


Link to post
Share on other sites
Quote:
all dumps of all active instances of all classes inherited by Object would execute their Dump() routines

The problem there is that you have to implement Dump() in every class, which is lots of tedious work. It's probably less effort to use the debug information that the compiler already provides (like in PDB files) if you want to dump the contents of objects, since you only have to write that code once (or, preferably, find someone else who's done it already) instead of for every class you ever write, and it can also provide useful information about local variables that helps when debugging (particularly when running on a computer without a proper debugger available).

There's also the problem of how Dump gets called - it's not entirely useful in the destructors because they won't be called if the program crashes fatally or if it has memory leaks. And by doing that or by storing storing a gigantic list of pointers to every Object that exists, you'll be missing the important relationships between objects - the output would be more useful if each class dumped its own primitive data and then recursively dumped all the other objects it has references to. (That doesn't need a virtual Dump method except when there's polymorphism (as in storing pointers to base classes instead of derived classes), and any genericity (e.g. functions that handle circular references) can be done with templates instead of inheritance.)

Also, in that particular example you could just as easily have a global variable (g_bMustDump) or a static variable in a class that nothing inherits from - the inheritance doesn't seem to add any functionality. (It does force every class to implement Dump - but it doesn't force every class to ever call Dump, so its limited requirement isn't useful.)

But it definitely is worthwhile adding debugging support to the engine, for times when you're not able to run inside a proper debugger - this just doesn't seem the best way of doing it [smile]

Share this post


Link to post
Share on other sites
Quote:
Original post by exorcist_bob
With EVERYONE here opposing me, I doubt this will make any difference.

It is my personal belief that it is hard to track where something went wrong, and causes your engine to crash, since a lot of times, you don't run a full-fledged game in the vc++ debugger. Since everything that handles a given set of data has the potential to mess it up, as an engine gets larger, it becomes harder and harder to track something down, and soon, you forget where it going and has gone. By forcing everything to dump data to a file, I believe it will be easier to track down and eliminate it. I think that the easiest way to do this is with a base class that all classes must inherit from. Since I can't call a pure virtual function in a base class, I have to call it from all classes that inherit from it.

*** Source Snippet Removed ***

Thanks,
exorcist_bob


Why is all that functionality in Object? Consider the following:
#include <iostream>
#include <ostream>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <functional>
#include <vector>

class dumping
{
template<class T>
friend class dumpable;

static std::vector<boost::function0<void> > on_dump;
public:
static void call(boost::function0<void> f)
{
f();
}
static void dump_all()
{
std::for_each(
on_dump.begin(),
on_dump.end(),
call );
}
};

std::vector<boost::function0<void> > dumping::on_dump;

class exception_base : public std::exception
{
public:
exception_base()
{
// An exception has occured, dump everything
dumping::dump_all();
}
};

class some_exception : public exception_base
{};

template<typename Parent = void>
class dumpable
{
virtual void dump() = 0;
public:
dumpable()
{
dumping::on_dump.push_back( boost::bind(&Parent::dump,*dynamic_cast<Parent*>(this) ) );
}
};

class a : public dumpable<a>
{
public:
void dump()
{
std::cout << "a dumping\n";
}
};
class b
{
public:
void dump()
{
assert (false);
}
};
class c : public dumpable<c>
{
public:
void dump()
{
std::cout << "c dumping\n";
}
};



int main()
{
try
{
a obj1;
b obj2;
c obj3;


throw some_exception();
}
catch(...)
{
std::cout << "Exception caught" << std::endl;
}

return 0;
}

It contains a base dumpable which automatically adds the dump function to a dynamic array which is called in case of an exception derived from our common exception base class. In the example b have chosen not to implement dumping and its dumping function is therefore not called. (note that my dumping::call might have a Boost or SC++L equivilant, but I can't remember one, and won't try to find it now)

Share this post


Link to post
Share on other sites

This topic is 4105 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this