Jump to content

  • Log In with Google      Sign In   
  • Create Account


Trienco

Member Since 22 Aug 2001
Offline Last Active Yesterday, 09:47 PM
-----

#4931137 Importance of structs?

Posted by Trienco on 14 April 2012 - 01:58 AM

While technically there is no real difference, you might sometimes find code where structs are used for plain data and classes are used when it also contains functions to operate on that data. So if you do want to make a difference, make it the distinction between data vs. behavior. Or in other words, if your thingy is supposed to "do" something, call it a class, if it's merely a dumb container for data, call it struct.


#4931116 Importance of structs?

Posted by Trienco on 13 April 2012 - 10:45 PM

You put data in structs. Especially if you need that data "as a whole". Imagine you have a bunch of things that can have an address (persons, shops, ...). You do NOT want to spam street/zip code/city/country as separate members into every single one of them. Most likely you will have functions that operate on addresses (printing them on labels, doing a sanity check). And again you wouldn't want each of these functions to take each value as a separate parameter. Even more so you may have a function that should create and RETURN an address. Functions simply can't return more than a single thing (unless you go with ugly workarounds like having half a dozen output parameters, which are an ugly concept that should be avoided whenever possible).

In short: whenever something belongs together (and will be used more than once), put it in a struct.


I guess this needs some example to demonstrate how utterly messy and unmanagable things would be without structs.

Start simple, you want to manage people. Let's say they have a name, age and gender. Let's also use a simple array instead of lists to store them. Also, have a function that can print a persion.

Without structs
string name[50];
int age[50];
int gender[50];

void printPerson(string name, int age, int gender)
{
   cout << name << age << gender;
}

or even some really bad style, that requires all your above arrays to be (semi)-global
void printPerson(int index)
{
  ///
   cout << name[index] << age[index] << gender[index];
}


With structs:
struct Person
{
   string name;
   int age;
   int gender;
};

Person people[50];

//I deliberately ignore the option to pass by reference
void printPerson(Person someone)
{
    cout << someone.name << someone.age << someone.gender;
}

//There is now no good reason to pass an index to the function.

Let's say you need a function to create a person or read one from a file


Without struct
void readPerson(File file, string* name, int* age, int* gender)
{
    *name = ...;
    *age = ...;
    *gender = ...;
}

using it would require this:

string name;
int age;
int gender;

readPerson(file, &name, &age, &gender);

If you had a struct, it could be easier

Person readPerson(File file)
{
    Person person;
    person.name = ...;
    person.age = ...;
    person.gender = ...;

    return person;
}

and using it would just be

Person person = readPerson(file);


Now let's add Addresses and Shops without using a struct for the address.

struct Person
{
   string name;
   string street;
   string zip;
   string city;
};

//Yes, this is easy to write AT FIRST by just copy/pasting stuff... and you will hate yourself for every single Ctrl+V when you need to add the country to the address EVERYWHERE

struct Shop
{
   Person owner;
   string name;
   string street;
   string zip;
   string city;
};

compare that to
struct Person
{
   string name;
   Address address;
};

struct Shop
{
   Person owner;
   string name;
   Address address;
};

In short, structs let you structure your code and avoid redundancy,usually resulting in lots of copy/paste. If you find yourself copy/pasting whole blocks of code, stop it and ask yourself why you even NEED to do that in the first place.

Structured code means less work, easy to maintain, extend and use. Also a lot less bugs, which is what happens when code becomes a huge mess where it gets increasingly hard to figure out what is happening when, where and how.


#4928117 how to force global variable which define in a static library to initialize?

Posted by Trienco on 04 April 2012 - 12:05 AM

One of my "favorite" issues that caused a lot of pain.

With VC++ you usually get away with actually referencing the variable somewhere (a dummy read in a destructor or whatever). GCC will actually delay it until the very first time the variable is actually accessed. So you can not rely on any side effects caused by the initialization to actually happen before main.

In short, trying something like this will usually not work:
bool classXisRegistered = registerClassX();

For VC++, you might get away with a simple reference somewhere
SomeClass::~SomeClass
{
	bool dummy = classXisRegistered;
}

For GCC we actually had to create static global objects that would have the desired side effect in the constructor. And based on all the weird rules about how a compiler may optimize, I wouldn't rely on even this to work for absolutely every compiler.

template
class Registrator
{
public:
   Registrator() { registerClass(); }
};

static const Registrator registerClassX;

By the way, this method still failed on another project, where this code was in a static lib that was used to build a dll. Somewhere along the way the linker still decided to drop it. They moved the entire code to be a direct part of the dll.

I played around and tested a few methods, which ended up in a somewhat convoluted test:
http://festini.devic...factorytest.zip

But I guess the easiest solution is to simply not rely on any side effects of global static variable initialization.


#4927498 Newbie here, I wish to start.

Posted by Trienco on 02 April 2012 - 08:03 AM

Though it would technically be possible to create a "turn based" Pong on the console with nothing but the standard library.

Text-based Pong ftw!
>>> you move your bat:
>>> 4
>>> units
>>> you miss the ball!
>>> **** YOU LOSE ****


Well, I was thinking more along the lines of an o for the ball and a couple |s for the bats. The reason I said "turn based" is because I don't know any non-standard way in C or C++ to get keyboard input without blocking.

TL;DR: try and be more open-minded. Nobody's forcing you to anything but it doesn't hurt to look beyond what one already knows and actually try it out long enough to get comfortable with it - who knows, you may actually like it!


To emphasize this point: I had co-workers with up to 40 years of experience in C that unfortunately spent at least 30 of them stubbornly refusing to learn anything new. Their code tended to be messy, not because you can't write clean code in C (you can), but because all the extra work resulted in taking shortcuts and just be sloppy to finish in time. So you had code with plenty of bugs that were also a pain to find in all that mess.

Of course I might simply be biased because I spent many years being the guy that had to find and fix bugs in other peoples code whenever the bug reports came in.

Nobody expects anyone to instantly transition to C++ and write what is so nicely called "modern C++". But flat out refusing even the option to make use of some extremely useful and time saving features out of a feeling of being "overwhelmed" by all the new possibilites? Really?


#4927376 Newbie here, I wish to start.

Posted by Trienco on 01 April 2012 - 11:22 PM

Because I find C++ too cluttered with all the OOP stuff,iterators, dynamic/static casts(with weird declarations, too),templates,maps, the list goes on. Whereas C is much easier, and typecasting is done via (type)variable and voila.


So in other words "I'd rather use a tooth pick for construction work than learning how to use those overrated 'power tools' everybody is talking about".

How is C "easier" if you end up wasting 80% of your time reinventing countless wheels instead of actually making progress with the game?

Based on your choice of language and current level of knowledge the only advice that comes to mind is: if you aim higher than a very simple Pong clone as a first project, you are over reaching. And even that means getting familiar with at least one way to get graphics on the screen (meaning a library) and reading keyboard input beyond scanf. Though it would technically be possible to create a "turn based" Pong on the console with nothing but the standard library.


#4927117 Forward declaration and a singleton class

Posted by Trienco on 01 April 2012 - 01:54 AM

Note the first line, you need to forward declaring there.


That's useless. A forward declaration simply says "a class of that name exists somewhere". It will NOT actually let you use that class, because the class is still undefined and it is completely unknown what is IN that class.

A forward declaration let's you do pretty much nothing but pass around pointers/references to that class. You still can't declare a variable of that type (because the size is unknown) and you can't call any functions of it (because they don't exist without actually DEFINING the class and what it looks like).

The simple matter of fact is that you can't "use" a forward declared class.

Fixing dependencies like that is usually based on NOT dumping your implementation in the header file, though in this case as SiCrane already said: DEFINE the class, before you try to use it.


#4926875 Smart pointers question

Posted by Trienco on 30 March 2012 - 11:32 PM

Basically have a single look-up table to see if the item is not deleted everytime you use it, like overriding the '->' operator. That way the ObjectFactory could still track the Gameobjects.


I see a tiny flaw in your concept. What is -> supposed to do if the object WAS deleted? Return 0, automatically have it dereferenced and crash? Or "secretly" redirect your call to a "null object" that simply does nothing?

I see only three scenarios.

a) You say "as long as anybody is still using that object, I must not delete it" -> use shared_ptr... you can still always remove it from whoever manages it, but you delay destruction until the user is done with it

b) You say "it is safe to assume by the time I clean up my table, nobody should be using that pointer anymore". In that case, use raw pointers or unique_ptr/auto_ptr (to clean up automatically when you remove the table)

c) You argue that "the user code should always make sure the pointer is still valid before using it". Use a single shared_ptr for the table and deal out weak_ptr to whoever uses it.

Or wrap it up in a way that returns a null object, if you really and absolutely don't want users to check the pointer. IF you can say "I don't care about nothing happening in that case. It still beats having to check the pointer every single time".

However, in a multi threaded situation (where objects might be used in more than one thread) I would always go with shared_ptr. Simply checking before use is worthless, when the object could be destroyed right between checking and using.


#4925523 Moving to a Point on a Circle

Posted by Trienco on 26 March 2012 - 10:13 PM

Uhm... maybe I'm missing something, but what exactly would be wrong with not putting up with any angles at all? There seems to be an unhealthy obsession with trigonometry where nobody needs it. (Also, all those confusing if's make my head hurt.. math for the most part doesn't generally doesn't work that way).

Look at what you're doing. You already KNOW the vector and instead of simply scaling it to the right length, you bash the problem with the trig club to calculate angles you then use to calculate a shorter version of that same vector. That is simply overcomplicating the problem with a pointless detour.

const Vector3 directionToCam = (Cam - Selected).normalize();
return Selected + (directionToCam * MaxDistance);


//If you really want to be on a circle (not a sphere):
//However keep in mind that if you move it to that position, you will no longer be centered on the object.

Vector3 directionToCam(Cam.x - Selected.x, 0, Cam.z - Selected.z);
Vector3 Calculated = Selected + (directionToCam.normalize() * MaxDistance);
Calculated.y = Cam.y;
return Calculated;



#4924911 OpenGL DrawArrays acting strangely

Posted by Trienco on 24 March 2012 - 10:27 AM

A (x or y) coordinate isn't a vertex and a quad only has 4 vertices. Why are you passing 8?


#4922175 Calculating CPU usage

Posted by Trienco on 14 March 2012 - 11:25 PM

I don't get it. Maybe my world view of how programs are executed is outdated on modern machines. Process is allowed to be executed by scheduler, process runs at CPU clock speed. They don't run faster or slower because "they use a lot of CPU". IPS also seems like a pointless concept. A div takes more cycles than an add. Does that mean a program using lots of divs executing for the same amount of time is using up less CPU, because you get much lower IPS?

You could use a clock that allows measuring actual process or even thread time (not sure what the equivalent is for Windows). A normal timer would not just tell you "this code was giving up its time slice more often or spent more time in blocking operations" it will also tell you "there might have been more higher priority processes active at the time or the scheduler simply had a bad day".

In the same way, stopping how long your code is executing only works for small parts of code and with multiple test runs (or by using a clock that only counts actual time spent in this thread). By just stopping the total time, you would also measure all the time spent in totally different processes whenever the scheduler interrupts you. As an alternative, you could set your thread to realtime priority, but you better make very sure you have a way of ending it and don't get stuck in some loop (or it's reset time).


#4920858 Confused a bit on the matrix math in 3D programming

Posted by Trienco on 10 March 2012 - 02:36 AM

Every object has a world matrix. This matrix is also very simple to read, because the four columns are nothing but "right","up","forward","position".

If you pretend that the camera is a regular object with a world matrix, the view matrix is simply the inverse of it. There is no "camera" and no way to actually change your point of view, You just move everything the opposite way.

Every object that is rendered will go through the view matrix, its world matrix and the projection matrix (or rather one single matrix you get by multiplying them all). 3D is nothing but taking 3D points, putting them through a bunch of matrices and getting a 2D point in screen coordinates. Simply speaking and ignoring all the extra stuff that makes 3D look cool and fancy.


#4919989 unique_ptr and fstream constructor problem

Posted by Trienco on 06 March 2012 - 11:42 PM

Somewhat hard/impossible to tell without seeing if your constructor ever bothers to actually assign an fstream in the first place. I also have absolutely no idea why you believe it necessary to use a different fstream in the first place. If open fails, just call open again WITHOUT ios::in.


#4919854 Message dispatching system - big problem with polymorphism

Posted by Trienco on 06 March 2012 - 12:37 PM

There's a way by using typeid/type_info and a map, which essentially boils down to map<type_info, handlerFunction>. While the implementation isn't overly pretty, the usage looks clean, for example

eventSystem.register(handlerFunction);

Using templates, the type of the handled event is automatically determined (because it's the parameter of the handler function). You will however not be able to use handler functions for any base classes, ie. you can't say handlerFunction(BaseMessage*) and have it handle all kinds of types. typeid will always compare the actual types.

In combination with boost::bind or a selfmade replacement it should work just fine.

You probably don't want to use it as is, because only one function per event type is kind of silly. You can use a multimap instead and the unregister function will require to pass the actual function you want to remove. Also, a function to remove all handler functions that are members of a specific object might be helpful.

Also, this code is mostly based on code I found online. I can't figure out where, but googling for TypeInfo wrapper events tells me that quite a few people used the same source...

class TypeInfo
{
public:
    explicit TypeInfo(const type_info& info) : _typeInfo(info) {}

    bool operator < (const TypeInfo& rhs) const    {
        return _typeInfo.before(rhs._typeInfo) != 0;
    }

private:
    const type_info& _typeInfo;
};

class Event
{
protected:
    Event() {}
    virtual ~Event() {}
};

class HandlerFunctionBase
{
public:
    virtual ~HandlerFunctionBase() {}
    void exec(const Event& event) {call(event);}

private:
    virtual void call(const Event&) = 0;
};

template < class T, class EventT >
class MemberFunctionHandler : public HandlerFunctionBase
{
public:
    typedef void (T::*MemberFunc)(const EventT&);
    MemberFunctionHandler(T* instance, MemberFunc memFn) : _instance(instance), _function(memFn) {}

    void call(const Event& event) { (_instance->*_function)(static_cast< const EventT& >(event)); }

private:
  T* _instance;
  MemberFunc _function;
};

template < class EventT >
class StaticFunctionHandler : public HandlerFunctionBase
{
public:
    typedef void (*Func)(const EventT&);
    StaticFunctionHandler(Func Fn) : _function(Fn) {}

    void call(const Event& event) { _function(static_cast< const EventT& >(event)); }

private:
  Func _function;
};

class EventHandler
{
public:
    ~EventHandler()
    {
        for ( Handlers::iterator it = _handlers.begin(); it != _handlers.end(); ++it )
            delete it->second;
        _handlers.clear();
    }

    void handleEvent(const Event& event)
    {
        Handlers::iterator it = _handlers.find(TypeInfo(typeid(event)));
        if( it != _handlers.end() )
            it->second->exec(event);
    }

    template < class D, class B, class EventT >
    void registerEventFunc(D* obj, void(B::*memFn)(const EventT&))
    {
        unregisterEventFunc<EventT>();
        _handlers[TypeInfo(typeid(EventT))] = new MemberFunctionHandler< B, EventT >(obj, memFn);
    }

    template < class EventT >
    void registerEventFunc(void(*Fn)(const EventT&))
    {
        unregisterEventFunc<EventT>();
        _handlers[TypeInfo(typeid(EventT))] = new StaticFunctionHandler< EventT >(Fn);
    }

    template <class EventT>
    void unregisterEventFunc()
    {
        Handlers::iterator it = _handlers.find(TypeInfo(typeid(EventT)));
        if( it != _handlers.end() )
        {
            delete it->second;
            _handlers.erase(it);
        }
    }

private:
    typedef std::map< TypeInfo, HandlerFunctionBase* > Handlers;
    Handlers _handlers;
};



#4918499 Attempting to start with OpenGL. Lots of failure

Posted by Trienco on 02 March 2012 - 12:00 AM

Get rid of those lines:

glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, 3);
glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, 3);
glfwOpenWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

It's way too easy to specify contradictory hints or something that simply isn't supported by your driver/hardware.


#4917303 Program keeps crashing

Posted by Trienco on 27 February 2012 - 11:38 PM

There are plenty of ways to crash, the most obvious being that you just go and delete an enemy (always the LAST one, no matter which name was entered) and don't seem to bother removing him from the list. Even worse, you don't even delete the enemy at all. This code shouldn't even compile, because you assign a string to a enemy*. Even if it was the correct pointer and you were deleting the enemy, the last line (which should probably be)
p_EnemyKill = 0;


is essentially pointless. pEnemyKill is a local variable, setting it to 0 before the end of the function doesn't do anything and most of all it won't affect ANY of the other pointers (especially the next pointer of the previous enemy). Instead of going on about copies and stuff, I'll just make a simple example:

int a = 5;
int b = a;
b = 6;

Is a now 6, just because b was set to the _value_ of b at first? Of course not.

Removing an entry from the middle of the list requires doing a lot more than that. You need to keep a pointer to the previous element so you can make it point to the element after the one to delete. There are also ways to avoid the special handling of head (which doesn't have a previous entry), but they involve a pointer to pointer and I would stay away from that for now.

There are also examples of code that is needlessly complex and could be shorter, cleaner AND more correct/robust.

 enemy* pFirstEnemy = m_pHead;

         cout << pFirstEnemy->getName() << endl;

         while(pFirstEnemy->getNext() != 0)
         {
                 pFirstEnemy = pFirstEnemy->getNext(); // gets the next object
                 cout << pFirstEnemy->getName() << endl; // returns name

         }


I notice at least two functions like that and you never check if the list is empty and a first element even exists. Once you killed the last enemy, this would crash. There is also no good reason to handle the first element separately like it was special in any way.


         enemy* pEnemy = m_pHead;     

         while(pEnemy)
         {
                 cout << pEnemy->getName() << endl; // returns name
                 pEnemy = pEnemy->getNext(); // gets the next object
         }
I also assume that this is your "list project" where you play with linked lists to understand how they work.. and then never ever do that again when you could just use std::list<Enemy> . Especially since muddling the class with list pointers might be the simple way for this example, but in a "real" class:

a) why would an object care if it is going to be stored in a list? (there might be exceptions, for example for pathfinding, though it would be more likely a prev pointer)
b) what if you decide the object needs to be in more than one list at a time?




PARTNERS