Skip to main content
gamedev.net
🔒 Locked

Member Functors

Started by NotAYakk Jun 8, 2006 at 2:34 PM 1 replies 800+ views
Original Post
NotAYakk
NotAYakk
So, I've been playing around with C++ template magic. Being touched in the head, I got to thinking about aspect oriented programming, functors, and classes. What if one used member functors to allow each function in a class to have private state -- and more importantly, to allow metafunction programming? The simplest examples include "this function should only run once", or "this function should count the number of times it runs", or "this function should aquire a write lock on the class during it's execution". I'm not certain if the result is worthwhile. But as a concept it looks interesting to me. :) Here is a quick stab at it:
#include <stdio.h>
#include <windows.h>

template<typename parentptr, typename retval>
class MemberFunctor {
  protected:
    parentptr const self;
    virtual retval Work() = 0;
    MemberFunctor(parentptr self_):self(self_) {}
    
  public:
    retval operator()() {
      return Work();
    }   
};

template<typename parentptr, typename retval>
class RunOnceMemberFunctor:
		public MemberFunctor<parentptr, retval>
{
  private:
    bool has_run;
    retval cached;
  protected:
		RunOnceMemberFunctor(parentptr self_):MemberFunctor<parentptr, retval>(self_), has_run(false), cached() {}
  public:
		retval operator()() {
			if (!has_run) {
				has_run = true;
				cached = Work();
			}
			return cached;
		}
};
class test {
    int data;
  public:
    class Init:public RunOnceMemberFunctor<test*, bool> {
			bool Work() {
				#ifdef _DEBUG
				OutputDebugString("Initialize called!\n");
				#endif
				self->data = 'init';
				return true;
			};
		public:
			Init(test* self_):RunOnceMemberFunctor<test*, bool>(self_){};
    } Initialize; 
    
    class Clean:public RunOnceMemberFunctor<test*, bool> {
			bool Work() {
				#ifdef _DEBUG
				OutputDebugString("Clean called!\n");
				#endif
				bool retval = self->data == 'init';
				self->data = 'gone';
				return retval;
			}
		public:
			Clean(test* self_):RunOnceMemberFunctor<test*, bool>(self_){};
    } CleanUp;
    
    class Value:public MemberFunctor<test*, int> {
			int call_count;
			int Work() {
				++call_count;
				#ifdef _DEBUG
				char tmp[1024] = {0};
				sprintf(tmp, "Called %d times, value 0x%08x\n", call_count, self->data);
				OutputDebugString(tmp);
				#endif
				return self->data;
			};
		public:
			Value(test* self_):MemberFunctor<test*, int>(self_), call_count(0) {};
    } Read;
    
    class Reference:public MemberFunctor<test*, int&> {
			int call_count;
			int& Work() {
				++call_count;
				#ifdef _DEBUG
				char tmp[1024] = {0};
				sprintf(tmp, "Called %d times, value 0x%08x\n", call_count, self->data);
				OutputDebugString(tmp);
				#endif
				return self->data;
			};
		public:
			Reference(test* self_):MemberFunctor<test*, int&>(self_), call_count(0) {};
    } Write;
    
    test():Initialize(this), CleanUp(this), Read(this), Write(this), data(0) {}
};

bool tester() {
	test a;
	int v = a.Read();
	bool b = a.Initialize();
	v = a.Read();
	b = a.CleanUp();
	v = a.Read();
	b = a.Initialize();
	v = a.Read();
	b = a.CleanUp();
	v = a.Read();
	a.Write() = 0xBAADF00D;
	v = a.Read();
	return true;
};

bool meh = tester();
The above compiles (with warnings) under MSVC++2003, and I suspect it is standards-ok C++ code. I'm not certain that it saves much typing, but I did manage to move metafunction logic out of the implementation of the function and into the declaration of the functor.
mikeman
mikeman
I think it can be pretty useful. I use something similar in my game: Objects have functors called Renderers that render the object. Currently, I render the terrain using a descedent of Renderer called DisplayListRenderer: The functor builds and displays the list the first time it gets executed, and the rest times it just displays it. The object has no idea how it's being rendered, all the relevant info(like the GL list id) is kept in the renderer.
NotAYakk
NotAYakk
Now, making a virtual functor... That can be trickey.

Pseudo-code:
template<typename parent, typename signature, typename data>struct VirtualFunctor: data {  private:     parent self;     typedef signature::retval(fun_type)(parent, data, signature::args);     fun_type* vfun;  signature::retval operator()(signature::args a) {    return vfun(self, *this, a);  }  VirtualFunctor(parent self_, fun_type* vfun):self(self_){}  void change_vfun(fun_type* vfun_){vfun=vfun_;}};


not quite perfect. I suspect I want to store a void* to the data that the child virtual functor wants to access, and use template magic to call the right function.

Or maybe just use boost::function. Heh.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.