Jump to content
  • Advertisement
Sign in to follow this  
marshdabeachy

Default constructor / function pointers

This topic is 4222 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

This would go in the beginner forum, but it appears to be down. And I'm not really a beginner anymore, but these are beginner problems that, for some rediculous reason, refuse to let me solve them. So here's the two issues: 1) Default constructors. You need them if you're not passing a class (or struct, in my case), any values during instantiation. I'm creating an instance of the struct (called Position) once, giving it the values I specified. Position is immediately getting passed into another function (the constructor for Button) by value. I get compiler errors, telling me I need a default constructor. Great. I give it default values to make it happy. So it finally compiles, runs... hits the Position constructor twice. The first is from the initial creation, which works fine. The second is in the Button constructor, on the function arguments line. I wasn't aware passing by value would call the constructor again. Copy constructor, yes (although I don't need a copy constructor since the struct is doing nothing with dynamic memory). Default constructor though? Well, it's hosing everything up, because that's what is being passed into the function. 2) Function pointers. I'm a bit rusty on this, so I looked it up. I got everything to compile... all looked good. I'm grabbing the address of a function (called Menu::CreateHostMenu) and passing it into another function (the constructor for Button). But upon running my program, I get this error:
Quote:
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
I checked the constructor Button, and about half the time it's being set properly, and the other half of the time it's being set to NULL. At all times it errors. Obviously something's screwy, but I can't figure out what. The problems are both in the same code. Here it is:
// button header
class Menu;

typedef void (Menu::*MenuFunct)(void);

struct Position {
	Position(unsigned x=0, unsigned y=0) : _x(x), _y(y) {}
	unsigned _x, _y;
};
struct Area {
	Area(unsigned w=0, unsigned h=0) : _w(w), _h(h) {}
	unsigned _w, _h;
};

class Button {
public:
	Button(std::string text, MenuFunct funct, Position pos, Area area);
private:
	std::string _text;
	MenuFunct _funct;
	Position _pos;
	Area _area;
};


// button implementation
Button::Button(std::string text, MenuFunct funct, Position pos, Area area) {
	this->_text = text;
	this->_funct = funct;
	this->_pos = pos;
	this->_area = area;
}


// menu implementation
void Menu::CreateMainMenu() {
	buttons.push_back( new Button( "Host Game", &Menu::CreateHostMenu, Position(100, 100), Area(100, 20) ) );
}


Am I missing something silly like usual? [Edited by - marshdabeachy on January 26, 2007 5:05:08 AM]

Share this post


Link to post
Share on other sites
Advertisement
You didn't understood the second creation of your Position struct: it's not created because you pass a position as a value, it is created because it is a sub-object of Button.

In fact, this is totally hidden from you, but the real code which is executed looks like:
Button::Button(list_of_params) : 
_text(), // calling the std::string default constructor
// _funct(), // well, don't do much; see the next remark
_pos(), // calling the Position default ctor
_area() // calling the Area default ctor
{
... blah blah
}

The _funct "ctor" does nothing (in fact, since it is a pointer (Plain Old Datatype or POD, for short), it is not even called).

When you enter the Button ctor, all the members have to be created. Since you don't initialize them, the default ctor of each sub-object is called. You can save some processing time by using an initializer list - you already know how to use it. I added the const reference thingy in the rewrite below.
Button::Button(const std::string& text, MenuFunct funct, const Position& pos, const Area& area) :
m_text(text), m_funct(funct), m_pos(pos), m_area(area)
{
// we don't need to do anything here!
}


BTW, the C++ standard denies you the right to use identifier names that begin with an underscore, as they are reserved for global usage by the implementation (read: the standard C++ library, the standard C library, and the compiler extensions). When it comes to member names, I usually prefix them using "m_", like this: m_funct, m_area, and so on.

Regarding your second question: how do you call the member function pointer? You must provide an object to call it, otherwise you won't be able to do it. Remember that a member function pointer invocation looks like:
(object.(*mem_fun))(arguments).
If the mem_fun type is R (C::mem_fun)(A1,...,AN), "object" must be of type C (or of a subtype of C).

Regards,

Share this post


Link to post
Share on other sites
Quote:
Original post by marshdabeachy
Am I missing something silly like usual?


Indeed.

(1) You don't need a default constructor at all. You need to learn about class invariants and initialization lists. A general rule is that you should avoid default constructors if you have non-default constructors since thy're a red flag that yuo either have a bad design or an poor implementation of your design (this is not a hard rule, but any time you think yuo need a default constructor when you didn't plan for one you should think hard. Very hard).

(2) A member function is not a function. A pointer-to-member-function is not a pointer-to-function. They are not interchangeable.

--smw

Share this post


Link to post
Share on other sites
Quote:
Original post by Emmanuel Deloget
BTW, the C++ standard denies you the right to use identifier names that begin with an underscore, as they are reserved for global usage by the implementation (read: the standard C++ library, the standard C library, and the compiler extensions). When it comes to member names, I usually prefix them using "m_", like this: m_funct, m_area, and so on.


This is incorrect. Names beginning with an underscore followed by an upper-case letter or names containing two consecutive underscores are reserved for the implementation, but names beginning with an underscore followed by a lower case letter are allowed.

That said, it's generally considered poor practice to start names with an underscore, mostly because many OS libraries pollute the global namespace with such names. It doesn't contravene any standard, but it'll burn the poor unsuspecting programmer just the same.

--smw

Share this post


Link to post
Share on other sites
Quote:
Original post by Bregma
Quote:
Original post by Emmanuel Deloget
BTW, the C++ standard denies you the right to use identifier names that begin with an underscore, as they are reserved for global usage by the implementation (read: the standard C++ library, the standard C library, and the compiler extensions). When it comes to member names, I usually prefix them using "m_", like this: m_funct, m_area, and so on.


This is incorrect. Names beginning with an underscore followed by an upper-case letter or names containing two consecutive underscores are reserved for the implementation, but names beginning with an underscore followed by a lower case letter are allowed.

Did I misunderstood §17.4.3.1.2? (that's not a rethorical question - it is actually possible that I misunderstood something here)
Quote:

- Each name that contains a double underscore (__) or begin with an underscore followed by an uppercase letter (2.11) is reserved to the implementation for any use.
- Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.(165)
...
(165): such names are also reserved in namespace ::std.

Emphasis is mine.

Share this post


Link to post
Share on other sites
The "for use as a name in the global namespace" is key. Since it's in the global namespace (or std), it's fine to use it in classes; it'll shadow any globals.

Share this post


Link to post
Share on other sites
Thanks guys. Emmanuel Deloget solved both problems. Actually, solving the first problem fixed the other one. It's always something silly. I gotta study up on initializer lists more.

To avoid any conflicts, I also changed the data members to use a different naming convention. :)

Share this post


Link to post
Share on other sites
Cripes, now what am I doing wrong? I add another parameter into the initializer list and the entire thing falls apart.


Button::Button(const std::string& text, MenuFunct funct, const Position& pos, const Area& area, ID3DXFont* font) :
b_text(text), b_funct(funct), b_pos(pos), b_area(area), b_font(font) {}



b_font and font are both of type ID3DXFont*. In the menu class, the font is being properly initialized. Or at least it has a valid pointer. I'm passing that pointer in the button class so they can use that font to render text. But when I go to render, I get a bad pointer. b_font is 0x00000064. On top of that, b_pos and b_area are corrupted.

Just one of those days, I guess... :(

Share this post


Link to post
Share on other sites
Okay, a little bit of progress... The font isn't the problem. The problem with the member function pointer still exists. How naive of me to think that after it reported the correct results the first time that it was actually working. I'll have to triple-check every before I believe it from now on.

The member function pointer is overwritting memory within the class. That's why the other data members are getting corrupted. Since I'm passing the exact same typedef everywhere, I'm not quite sure why it's going bad. I did some checking on the size of the member function pointer... In the constructor for Button, the size of the pointer is 16 bytes. However, when I get to the render function, the exact same unmodified pointer is now 4 bytes.

:-/

I did a bit of research, and turned up this busted page that Google has a cache on:

http://72.14.205.104/search?q=cache:kJsoal2a3sIJ:www.arcknowledge.com/gmane.comp.lang.c%2B%2B.tips/2002-09/msg00000.html+member+function+pointer+size&hl=en&gl=us&ct=clnk&cd=9

Apparently forward declarations are to blame? The Menu class requires the Button class, and the Button class requires the menu class, so I had to use forward declarations to get it to compile. Is that what's causing this problem? Any way to fix it?

[edit]

Okay, fine... nevermind. I moved header files and forward definitions this-way and that in a fury of coding prowess, and it eventually started working. *crosses fingers it really is working*

[Edited by - marshdabeachy on January 26, 2007 8:19:02 PM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!