Question

My previous question about this subject was answered and I got some tests working nice. Map functions of a class

My question is now, if there is a way to while declaring the function, be able to register it in a map, like I realized in this question about namespaces and classes: Somehow register my classes in a list

the namespaces and classes was fine to register in a map using the "static" keyword, with that, those static instances would be constructed before the main() be called.

Can I do that somehow with class functions?
because when I use static keyword inside a class declaration, I can't initialize the member as I can outside the class declaration(as with namespaces and classes in the second url above)

I guess I could hardcode all members inside the constructor and register them in a map, but I would like to know if there is a way to do that while I declare the members, to make it easier in the future

Thank you,
Joe

Was it helpful?

Solution

What is your problem here ?

The problem is that, unfortunately, in C++ functions are not considered first class members.

Oh sure there are those pointers to functions that work pretty well, but there is no generic function type or anything like that.

There are however ways to work around this, the simplest I think being the Command pattern.

In the Command pattern a function (operation) is abstracted away in an object. The arguments are stored in the object for later reuse (for example undo or redo command) and a unified interface exists to perform the operation itself.

Less talk, more code:

class Command
{
public:
  virtual ~Command() {}

  virtual Command* clone() const = 0;

  virtual void execute() = 0;
};

Simple ?

class Foo {};

class FooCommand: public Command
{
public:
  void parameters(Foo& self, int a, std::string const& b);

  virtual FooCommand* clone() const;
  virtual void execute();

private:
  Foo* m_self;
  int m_a;
  std::string const* m_b;
};

Now, the sweet thing is that I can simply store my command in a map.

 // registration
 typedef boost::ptr_map<std::string, Command> commands_type;

 commands_type commands;
 commands.insert("foo", FooCommand());

 // get the command
 Foo foo;
 FooCommand* cFoo = dynamic_cast<FooCommand*>(commands["foo"].clone());
 if (cFoo != 0)
 {
   cFoo->parameters(foo, 2, "bar");
   cFoo->execute();
 }

This proposal would still require some work.

  • passing the parameters is quite annoying since it requires a down cast.
  • I did not concern myself with exception safety, but returning an auto_ptr or a shared_ptr would be better for the clone method...
  • the distinction between a const and non-const Foo argument is not that easy to introduce.

However it is safer than using a void* to store the pointers to function in you map since you have the advantage of RTTI to check whether or not the type is correct.

On the other hand, printing the collection of Commands linked to a particular object is incredibly easy now (if you have one map per object), you can also find ways to emulate the effect of virtual methods etc...

But I hope you realize that you are in fact trying to implement reflection, and it's not gonna be easy... good luck!

OTHER TIPS

You could use the preprocessor to allow code such as the following:

#include <iostream>

#include "Registration.h"

class myclass {
  public:
  myclass() { HANDLE_REGISTRATION(); }

  private:
  static void reg1()  { std::cout << "reg1" << std::endl; }
  static void reg2()  { std::cout << "reg2" << std::endl; }
  static void unreg() { std::cout << "ERROR!" << std::endl; }

  BEGIN_REGISTRATION();
    REGISTER(reg1);
    REGISTER(reg2);
  END_REGISTRATION();
};

int main()
{
  myclass obj;
  obj.callAllRegistered();

  return 0;
}

The ugly preprocessor hacks are hidden away in Registration.h:

#ifndef INCLUDED_REGISTRATION_H
#define INCLUDED_REGISTRATION_H

#include <string>
#include <map>

#define BEGIN_REGISTRATION() \
  std::map<std::string, void(*)()> reg; \
  void register_static(const std::string& name, void(*f)()) \
  { \
    reg[name] = f; \
  } \
  void registerAll() {

#define REGISTER(name) register_static(#name, name)

#define HANDLE_REGISTRATION() registerAll()

#define END_REGISTRATION() \
  } \
  public: \
  void callAllRegistered() { \
    std::map<std::string,void(*)()>::const_iterator it; \
    for (it = reg.begin(); it != reg.end(); ++it) \
      it->second(); \
  } \
  private: \
  typedef int unusedblahblahblah___

#endif

What you are seeking is a principle called Reflection. Unfortunately, C/C++ does not provide this functionality, and implementing it in a C++ object would prove very complicated (if it's even possible).

If this functionality is needed, I would suggest looking at another language that supports metaprogramming features like this. Doing this exact thing is trivial in some other languages. For example, in Ruby you could say:

class Myclass
  def initialize
  end

  def a
  end

  def b
  end
end

x = Myclass.new
x.methods
=> ["inspect", "b", "clone", "taguri", "public_methods", "display", "instance_va
riable_defined?", "equal?", "freeze", "taguri=", "methods", "respond_to?", "dup"
, "instance_variables", "to_yaml_style", "__id__", "method", "eql?", "id", "sing
leton_methods", "send", "taint", "frozen?", "instance_variable_get", "__send__",
"instance_of?", "to_a", "type", "to_yaml_properties", "protected_methods", "obj
ect_id", "instance_eval", "==", "===", "instance_variable_set", "to_yaml", "kind
_of?", "extend", "to_s", "a", "hash", "class", "tainted?", "=~", "private_method
s", "nil?", "untaint", "is_a?"]

This will list all of the member functions (many of them are automatically-generated in this case) associated with the object. The same can be done for instance variables, etc. Many other languages offer these types of features.

If this feature is critical to what you are doing, then I would recommend that you re-examine your choice of programming language as you seem to be wanting to work on a higher level than C/C++ are typically designed for. It may be possible to shoehorn this sort of thing into C++ by using some sort of object/class generator pattern but it would not be trivial to write or to use the resulting classes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top