Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » working with allegro datatypes in c++

Credits go to 23yrold3yrold, Archon, Elverion, and Kibiz0r for helping out!
This thread is locked; no one can reply to it. rss feed Print
working with allegro datatypes in c++
relay01
Member #6,988
March 2006
avatar

I've noticed that I can't have a bitmap or allegro datatype contained within a class. or at least whenever I try it I get "c++ iso standards forbid" compile errors.

Am i missing a way to do this or is it impossible in allegro?

_____________________________________

23yrold3yrold
Member #1,134
March 2001
avatar

Quote:

I've noticed that I can't have a bitmap or allegro datatype contained within a class.

The hell you can't. Post code.

--
Software Development == Church Development
Step 1. Build it.
Step 2. Pray.

relay01
Member #6,988
March 2006
avatar

Quote:

The hell you can't. Post code.

okay

1 
2class Sprite
3{
4 public:
5 Sprite(int pos_x, int pos_y, BITMAP* sprit); //Single Image Type Sprite
6 Sprite(int pos_x, int pos_y, DATAFILE* datf); //multiimage type sprite
7
8 int update_pos(int pos_x, int pos_y); //for sprites with no collide information just position
9 int update_pos(Collision & spr_collide, int pos_x, int pos_y); //for sprites with collide information
10
11 int draw(BITMAP* &buffer, BITMAP* image);
12 int draw(BITMAP* &buffer,DATAFILE* datafile, int number);
13
14 int set_animation(int startframe, int endframe);
15 int animate(BITMAP* &buffer, DATAFILE*datafile);
16
17 DATAFILE* datafile = NULL;
18 
19 private:
20
21 //Sprite dimension information
22 int x_pos;
23 int y_pos;
24 int width;
25 int height;
26
27 int is_image; //0 if datafile 1 if image type sprite
28
29 //collision related information
30 int place_in_collision_vector; //keeps track of this particular sprites place in the collision vector
31 int added_to_vector; //if this sprite is part of a vector type Collision model, this is changed to 1
32 int collision_is_vector; //tracker for collision model
33
34 //animation related data values
35 int current_frame; //frame that sprite is currently on
36 int start_frame; //frame that sprite starts with
37 int end_frame; //frame that sprite ends with
38};

when compiled will give the ISO C++ error...

_____________________________________

23yrold3yrold
Member #1,134
March 2001
avatar

Depends what it forbids. Does it forbid using it before it's declared? Do you include allegro.h before that header? I think winalleg.h might cause a funny conflict like that too ...

--
Software Development == Church Development
Step 1. Build it.
Step 2. Pray.

Elverion
Member #6,239
September 2005
avatar

DATAFILE* datafile = NULL;

You can't do that. The = cannot be used in that fashion for classes. You must use constructors instead.

class MyClass
{
  public:
    DATAFILE *mydata;
    MyClass();
}

MyClass::MyClass()
{
  mydata = NULL;
}

Also, this is more stylistic than necessary, but you normally do not put the variable names in the declaration of functions (only the variable type is necessary). Leave that for the definitions.

EDIT:

int draw(BITMAP* &buffer, BITMAP* image);

You also shouldn't use * and & at the same time...It's either one or the other. In this case, you want to use * because you will be passing a pointer.

--
SolarStrike Software - MicroMacro home - Automation software.

Kibiz0r
Member #6,203
September 2005
avatar

Anything that tells you what the class is, goes in the .hpp file, and in the class structure. Anything that does stuff, goes in the .cpp file, and not in the class structure. (unless you want it inlined)

Quote:

Also, this is more stylistic than necessary, but you normally do not put the variable names in the declaration of functions (only the variable type is necessary). Leave that for the definitions.

I disagree with this. Since the header file is generally your reference for how to use the class, wouldn't it be helpful to have descriptive parameter names to make it clear how it should be used?

This:

int draw(BITMAP* &, BITMAP*);

certainly tells you less about the function than this:

int draw(BITMAP* &buffer, BITMAP* image);

The only time I don't name my parameters is when a function is to never be used. For example:

class DoNotCopy
{
private:
    DoNotCopy(const DoNotCopy&);
    ...
};

Also, to echo Elverion, there are a few instances where you would want to pass a pointer by reference. This, however, is most likely not one of them. ;)

Archon
Member #4,195
January 2004
avatar

Quote:

You can't do that. The = cannot be used in that fashion for classes. You must use constructors instead.

MyClass::MyClass()
{
  mydata = NULL;
}

It's actually better to use 0 instead of NULL in C++, and it's better to set the initial value in the constructor list, like so:

MyClass::MyClass() : mydata(0)
{
}

relay01
Member #6,988
March 2006
avatar

i appreciate the responses... it appears to be working just as long as I don't set the value to anything... If anybody wants overtime... I'd like some advice on creating a copy constructor...

_____________________________________

Kibiz0r
Member #6,203
September 2005
avatar

Copy constructors are in the form of:

Example(const Example& other)

All classes have a default copy constructor that is genoratored by the compiler based on the member fields. If I have a class like this:

class Example
{
private:
    int x;
};

The compiler tacks on this code, among other things:

class Example
{
private:
    int x;

public:
    Example(const Example& other)
    :   x(other.x)
    {}
};

So when we do this:

Example a;
Example b(a);

b now has the same value of x as a.

So what you want to do is write a constructor that takes a reference-to-const of another instance of the same class as a parameter. Then you want to do whatever is necessary to enforce a predictable behavior from the copy constructor.

For most cases, the default constructor should be fine. But sometimes, you have certain concepts of ownership that have to be communicated or special consideration has to be taken because of the contained types.

Side-note: Also, if a contained type has no copy constructor -- perhaps it has been disallowed by making it private, you must specify your own because the compiler cannot do it for you.

Back to ownership/special consideration...
Consider this class:

1class Bitmap
2{
3private:
4 BITMAP* m_bmp;
5 
6public:
7 Bitmap(const std::string& _path)
8 {
9 m_bmp = load_bitmap(_path.c_str());
10 assert(m_bmp && "Haha, no bitmap loaded!");
11 }
12 
13 Bitmap(const Bitmap& _other)
14 : m_bmp(_other.m_bmp)
15 {}
16 
17 ~Bitmap()
18 {
19 destroy_bitmap(m_bmp);
20 }
21};

Looks fine, right?

NOOOOOO!!!!!

int main()
{
    Bitmap a("my bitmap.bmp");   //ok, make a new Bitmap
    {                            //open a new scope
        Bitmap b(a);             //ok, copy the bitmap from a
    }
    //PROBLEM: b has gone out of scope and the destructor has been called
    //Therefore, m_bmp no longer exists!
    a.DoStuff()   //this will then crash
}

So we need a copy constructor like this:

Bitmap::Bitmap(const Bitmap& _other)
:   m_bmp(create_bitmap(_other.m_bmp->w, _other.m_bmp->h))
{
    blit(_other.m_bmp, m_bmp, 0, 0, 0, 0, _other.m_bmp->w, _other.m_bmp->h);
}

Of course, this is a shallow solution. A robust solution would keep a count of references to the bitmap, like boost::shared_ptr, or boost::intrusive_ptr, and delete it when nobody else needs it. But this would be too much code to write to demonstrate a simple concept. :P

relay01
Member #6,988
March 2006
avatar

Kibiz0r, thank you for helping me avoid a HUGE bug.

_____________________________________

Kibiz0r
Member #6,203
September 2005
avatar

If that saved you trouble, try this:

1class Bitmap
2{
3private:
4 class BmpWrapper
5 {
6 private:
7 BmpWrapper(const BmpWrapper&);
8 BmpWrapper& operator=(const BmpWrapper&);
9 
10 BITMAP* m_bmp;
11 
12 public:
13 BmpWrapper(const std::string& _path)
14 {
15 m_bmp = load_bitmap(_path.c_str(), NULL);
16 assert(m_bmp);
17 }
18 
19 ~BmpWrapper()
20 {
21 destroy_bitmap(m_bmp);
22 }
23 };
24 
25 boost::shared_ptr<BmpWrapper> m_pBmp;
26 
27public:
28 Bitmap(const std::string& _path)
29 : m_pBmp(new BmpWrapper(_path))
30 {}
31};

The default copy constructor and operator= will work as expected because of the semantics of boost::shared_ptr. When all the shared_ptrs that reference the same BmpWrapper are gone, the BITMAP* will be destroyed. This way avoids the memory overhead and CPU time of constantly copying BITMAPs, but is still safe.

Note that boost::shared_ptr uses shallow reference counting, which is stored in the boost::shared_ptr itself. So if you make two instances of Bitmap which have the same path, you will have two copies of the same Bitmap. Only copy construction and operator= give you an all-access pass to the wonderful world of managed memory.

You can get around that using boost::intrusive_ptr, but then you have to have some central repository of BmpWrappers that are probably organized by a hash of the path string, and you have to define a few functions that Boost needs to use in order to know when to delete them, etc... it's a lot of work to save a handful of KB.

Go to: