views:

233

answers:

4

Hi,

My previous question about this subject was answered and I got some tests working nice. http://stackoverflow.com/questions/1786809/c-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: http://stackoverflow.com/questions/1691473/c-c-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

+1  A: 

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
Greg Bacon
and it is ungly
stefanB
You still have to manually register all the functions, though. Plus, hacking it together in the pre-processor leaves many openings for non-obvious problems, like how `END_REGISTRATION` changes the current access level to `private` regardless of what the access level was before it was called.
bta
+1  A: 

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.

bta
Ruby is interpretated, right? I know that C/C++ aren't supposed to go for that, since there are higher languages like Ruby and C# that easily give you reflection, but I really like C++ and I prefer using that, even if its harder to accomplish in this subject.
Jonathan
I won't say it's impossible to accomplish, since other languages (like Ruby) use interpreters built in C/C++. However, you will end up doing a lot of work, as you will taking a big step toward designing and implementing a new programming language. Matthieu M. had a good idea, but as you can see it gets complicated very quickly (and with added complexity comes a greater potential for problems and increased debugging difficulty).
bta
A: 

so this can't be done with the help of macros?

Jonathan
+1  A: 

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!

Matthieu M.