How to use class functions as a variable?

Started by
17 comments, last by Decrius 13 years, 5 months ago
With luabind you had to create functions to get and set variables in a class that you wanted to bind. This actually was very useful because it allowed me to create a really handy window class for lua but now I would like to use the class in C++ without needing to use get and set functions every time I want to change something.

Is there a way to use get and set functions as variables? Like this:

class foo{public:  void set_val( float in )  {    val = in;  }  float get_val()  {    return val;  }private:  float val;};


I want those two functions to act as though I were directly interacting with the variable.
Advertisement
That's not possible in standard C++. However, your compiler may have extensions to do that. Ex: MSVC's __declspec(property).
Quote:Original post by SiCrane
That's not possible in standard C++. However, your compiler may have extensions to do that. Ex: MSVC's __declspec(property).


Really? Well I don't really think it would be a good idea to use functions that only work with one compiler since I am hoping to release it as an open source library.

Is there absolutely no other way to trigger a function and or block of code when a variable is set or read from? This actually seems hard to believe...
You can use Luabind properties to bind the variable without writing get set functions.
Quote:Original post by Kambiz
You can use Luabind properties to bind the variable without writing get set functions.


Not for what I was doing. I was not just setting a float or int.
The one way I can think of, which is very ugly, is to create a property class:

// untestedtemplate<typename T>class property{    public:        property(const boost::function<T &()> &get,                  const boost::function<void (const T &)> &set,                 const T &init = T()) :            get_(get),            set_(set)        {            set_(init);        }        T &operator= (const T &val)        {            set_(val);            return get_();        }        const operator T &() const { return get_(); }    private:        boost::function<T &()> get_;        boost::function<void (const T &)> set_;};


But that's ugly and heavy weight and I wouldn't recommend it. I'm showing you to put you off the idea :)

But also, perhaps step back a moment and ask yourself how many times such an interface is really desirable. The thing I don't like about properties is that the syntax suggests a simple, quick operation, when in fact anything could be happening.

I find they can be handy e.g. in Python (where there's first class support for them) on the rare occasion where you start out with a 'public' variable and it later transpires that this variable needs to be part of some invariant that didn't exist before. In this case, you can replace the variable with a property and not break the client interface.

However, would it really hurt you, in this case to simply use a public variable? Ask yourself how many people will be using the code and how widely. And ask yourself about how likely it is that the variable might be involved in some as-yet-unspecified invariant down the road. I'd posit that people put far too much stead in public/private/protected encapsulation and they can sometimes end up creating more of a development burden than would a simple public variable. Not all the time, but sometimes. I get the feeling this could be one of those times.
Quote:Original post by SteveDeFacto
Not for what I was doing. I was not just setting a float or int.


Luabind can call almost arbitrary code for the get/set functionality. It's in the link Kambiz posted.
Quote:Original post by the_edd
The one way I can think of, which is very ugly, is to create a property class:

// untestedtemplate<typename T>class property{    public:        property(const boost::function<T &()> &get,                  const boost::function<void (const T &)> &set,                 const T &init = T()) :            get_(get),            set_(set)        {            set_(init);        }        T &operator= (const T &val)        {            set_(val);            return get_();        }        const operator T &() const { return get_(); }    private:        boost::function<T &()> get_;        boost::function<void (const T &)> set_;};


But that's ugly and heavy weight and I wouldn't recommend it. I'm showing you to put you off the idea :)

But also, perhaps step back a moment and ask yourself how many times such an interface is really desirable. The thing I don't like about properties is that the syntax suggests a simple, quick operation, when in fact anything could be happening.

I find they can be handy e.g. in Python (where there's first class support for them) on the rare occasion where you start out with a 'public' variable and it later transpires that this variable needs to be part of some invariant that didn't exist before. In this case, you can replace the variable with a property and not break the client interface.

However, would it really hurt you, in this case to simply use a public variable? Ask yourself how many people will be using the code and how widely. And ask yourself about how likely it is that the variable might be involved in some as-yet-unspecified invariant down the road. I'd posit that people put far too much stead in public/private/protected encapsulation and they can sometimes end up creating more of a development burden than would a simple public variable. Not all the time, but sometimes. I get the feeling this could be one of those times.


It's a window class that has to interact with the windows API which is not as simple as just setting a variable. Here are some of the functions I turned into variables:

void E_WINDOW::set_title( std::string title ){	SetWindowText( hWnd, title.c_str() );}std::string E_WINDOW::get_title(){	char* title = NULL;	GetWindowText( hWnd, title, 256 );	return title;}void E_WINDOW::set_icon( std::string file ){	HANDLE icon = LoadImage(NULL, file.c_str(), IMAGE_ICON, 0, 0, LR_LOADFROMFILE);	SendMessage(hWnd, (UINT)WM_SETICON, ICON_BIG, (LPARAM)icon);	IconFileName = file;}std::string E_WINDOW::get_icon(){	return IconFileName;}void E_WINDOW::set_left( int left ){	RECT rect;	GetWindowRect( hWnd, &rect );	SetWindowPos( hWnd, NULL, left, rect.top, (rect.right - rect.left), (rect.bottom - rect.top), NULL);}int E_WINDOW::get_left(){	RECT rect;	GetWindowRect( hWnd, &rect );	return rect.left;}void E_WINDOW::set_top( int top ){	RECT rect;	GetWindowRect( hWnd, &rect );	SetWindowPos( hWnd, NULL, rect.left, top, (rect.right - rect.left), (rect.bottom - rect.top), NULL);}int E_WINDOW::get_top(){	RECT rect;	GetWindowRect( hWnd, &rect );	return rect.top;}void E_WINDOW::set_width( int width ){	RECT rect;	GetWindowRect( hWnd, &rect );	SetWindowPos( hWnd, NULL, rect.left, rect.top, width, (rect.bottom - rect.top), NULL);	SwapChain->Resize();}int E_WINDOW::get_width(){	RECT rect;	GetWindowRect( hWnd, &rect );	return rect.right - rect.left;}void E_WINDOW::set_height( int height ){	RECT rect;	GetWindowRect( hWnd, &rect );	SetWindowPos( hWnd, NULL, rect.left, rect.top, (rect.right - rect.left), height, NULL);	SwapChain->Resize();}int E_WINDOW::get_height(){	RECT rect;	GetWindowRect( hWnd, &rect );	return rect.bottom - rect.top;}void E_WINDOW::set_visible( bool visible ){	long style = GetWindowLongPtr(hWnd, GWL_STYLE);	bool isset = !!(style & WS_VISIBLE);	if (visible)	{		if (!isset)		{			SetWindowLongPtr( hWnd, GWL_STYLE, style | WS_VISIBLE );			SetWindowPos( hWnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_DRAWFRAME );		}	}	else	{		if (isset)		{			SetWindowLongPtr( hWnd, GWL_STYLE, style & ~WS_VISIBLE );			SetWindowPos( hWnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_DRAWFRAME );		}	}}bool E_WINDOW::get_visible(){	long style = GetWindowLongPtr(hWnd, GWL_STYLE);	bool isset = !!(style & WS_VISIBLE);	return isset;}void E_WINDOW::set_maximize( bool maximize ){	long style = GetWindowLongPtr(hWnd, GWL_STYLE);	bool isset = !!(style & WS_MAXIMIZEBOX);	if (maximize)	{		if (!isset)		{			SetWindowLongPtr( hWnd, GWL_STYLE, style | WS_MAXIMIZEBOX );			SetWindowPos( hWnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_DRAWFRAME );		}	}	else	{		if (isset)		{			SetWindowLongPtr( hWnd, GWL_STYLE, style & ~WS_MAXIMIZEBOX );			SetWindowPos( hWnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_DRAWFRAME );		}	}}bool E_WINDOW::get_maximize(){	long style = GetWindowLongPtr(hWnd, GWL_STYLE);	bool isset = !!(style & WS_MAXIMIZEBOX);	return isset;}void E_WINDOW::set_minimize( bool minimize ){	long style = GetWindowLongPtr(hWnd, GWL_STYLE);	bool isset = !!(style & WS_MINIMIZEBOX);	if (minimize)	{		if (!isset)		{			SetWindowLongPtr( hWnd, GWL_STYLE, style | WS_MINIMIZEBOX );			SetWindowPos( hWnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_DRAWFRAME );		}	}	else	{		if (isset)		{			SetWindowLongPtr( hWnd, GWL_STYLE, style & ~WS_MINIMIZEBOX );			SetWindowPos( hWnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_DRAWFRAME );		}	}}bool E_WINDOW::get_minimize(){	long style = GetWindowLongPtr(hWnd, GWL_STYLE);	bool isset = !!(style & WS_MINIMIZEBOX);	return isset;}void E_WINDOW::set_buttons( bool buttons ){	long style = GetWindowLongPtr(hWnd, GWL_STYLE);	bool isset = !!(style & WS_SYSMENU);	if (buttons)	{		if (!isset)		{			SetWindowLongPtr( hWnd, GWL_STYLE, style | WS_SYSMENU );			SetWindowPos( hWnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_DRAWFRAME );		}	}	else	{		if (isset)		{			SetWindowLongPtr( hWnd, GWL_STYLE, style & ~WS_SYSMENU );			SetWindowPos( hWnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_DRAWFRAME );		}	}}bool E_WINDOW::get_buttons(){	long style = GetWindowLongPtr(hWnd, GWL_STYLE);	bool isset = !!(style & WS_SYSMENU);	return isset;}void E_WINDOW::set_caption( bool caption ){	long style = GetWindowLongPtr(hWnd, GWL_STYLE);	bool isset = !!(style & WS_CAPTION);	if (caption)	{		if (!isset)		{			SetWindowLongPtr( hWnd, GWL_STYLE, style | WS_CAPTION );			SetWindowPos( hWnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_DRAWFRAME );		}	}	else	{		if (isset)		{			SetWindowLongPtr( hWnd, GWL_STYLE, style & ~WS_CAPTION );			SetWindowPos( hWnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_DRAWFRAME );		}	}}bool E_WINDOW::get_caption(){	long style = GetWindowLongPtr(hWnd, GWL_STYLE);	bool isset = !!(style & WS_CAPTION);	return isset;}void E_WINDOW::set_opacity( float val ){	opacity = val;	SetLayeredWindowAttributes( hWnd, 0, (BYTE)(255 * opacity), LWA_ALPHA );}float E_WINDOW::get_opacity(){	return opacity;}void E_WINDOW::set_size( bool size ){	long style = GetWindowLongPtr(hWnd, GWL_STYLE);	bool isset = !!(style & WS_SIZEBOX);	if (size)	{		if (!isset)		{			SetWindowLongPtr( hWnd, GWL_STYLE, style | WS_SIZEBOX );			SetWindowPos( hWnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_DRAWFRAME );		}	}	else	{		if (isset)		{			SetWindowLongPtr( hWnd, GWL_STYLE, style & ~WS_SIZEBOX );			SetWindowPos( hWnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_DRAWFRAME );		}	}}bool E_WINDOW::get_size(){	long style = GetWindowLongPtr(hWnd, GWL_STYLE);	bool isset = !!(style & WS_SIZEBOX);	return isset;}void E_WINDOW::set_show_cursor(bool state){	show_cursor = state;	POINT MousePos;	GetCursorPos(&MousePos);	if ( hWnd == WindowFromPoint(MousePos) )	{		if ( state )		{			SetCursor(LoadCursor( NULL, IDC_ARROW ));		}		else		{			SetCursor(NULL);		}	}}bool E_WINDOW::get_show_cursor(){	return show_cursor;}void E_WINDOW::set_bgcolor(luabind::object color){	bgcolor = D3DXCOLOR( luabind::object_cast<float>(color[1]), luabind::object_cast<float>(color[2]), luabind::object_cast<float>(color[3]), luabind::object_cast<float>(color[4]) );}luabind::object E_WINDOW::get_bgcolor(){	luabind::object table = luabind::newtable( g_Lua );	table[1] = bgcolor.r;	table[2] = bgcolor.g;	table[3] = bgcolor.b;	table[4] = bgcolor.a;	return table;}


As you could imagine it made working with a window super easy! I also had it initialize the windows with default values which meant I only needed to set values that I wanted different then the default.
Quote:Original post by SteveDeFacto
Quote:Original post by Kambiz
You can use Luabind properties to bind the variable without writing get set functions.


Not for what I was doing. I was not just setting a float or int.


So you are doing something non trivial but like it to look like a simple assignment in C++? That will just cause confusion, especially if you want to make the code available as a library.

If you want the syntactic sugar implement it in Lua (Luabind can do this) but don't hack it into C++. The easy of use an advantage of Lua, but in C++ one expects the low level interface.

Quote:Original post by SteveDeFacto
It's a window class that has to interact with the windows API which is not as simple as just setting a variable. Here are some of the functions I turned into variables:

*** Source Snippet Removed ***

As you could imagine it made working with a window super easy! I also had it initialize the windows with default values which meant I only needed to set values that I wanted different then the default.


I'd say stick with what you have, especially as some of the functions are non-trivial. The verbosity of C++ is something you'll have to live with.

This topic is closed to new replies.

Advertisement