Solved

MFC and factory method

Posted on 2004-09-22
40
607 Views
Last Modified: 2013-12-14
I went to an interveiw, in which the interviewer stated that MFC uses class factory method.

I don't believe this to be true, but I wasn't sure if the interviewer really believed this, or was looking to see if I would correct him.

Does any one here believes that MFC uses class factory method, and if so, can you give an example of where and how?

The interviewer also believed that by using a class factory method, you would not have to use switch/case or if statements to determine what child type to create.

All of the class factory examples I've seen still use switch statements or if conditions.

Is there someway that using the class factory method helps to avoid using switch statements or if-conditions?
0
Comment
Question by:Hollaceluna_
  • 20
  • 14
  • 5
  • +1
40 Comments
 
LVL 30

Assisted Solution

by:Axter
Axter earned 200 total points
ID: 12121659
>>Is there someway that using the class factory method helps to avoid using switch statements or if-conditions?

If you use the registry method, you wouldn't have to use switch or if-conditions.
0
 
LVL 30

Expert Comment

by:Axter
ID: 12121671
Even in the registry method, you're really hiding the if-conditon behind map class code
0
 
LVL 30

Expert Comment

by:Axter
ID: 12121675
>>Does any one here believes that MFC uses class factory method, and if so, can you give an example of where and how?

IMHO, No.
0
 
LVL 17

Assisted Solution

by:rstaveley
rstaveley earned 150 total points
ID: 12123511
I'm not too hot at design patterns, but I got the impression that a factory method is most typically used when you have a dynamically loadable class from a 3rd party vendor.

A good example is a SAX parser that conforms to a standard interface (i.e. is derived from an abstract class). All SAX parsers are derived from an abstract class (i.e. interface), which gives you (say) startElement, endElement, characters etc. The loadable driver provides you with code for the vendor's implementation of the class derived from abstract class and, critically, a class factory which returns a pointer to an instance of the vendor's class derived from the abstract class. Java has a nice mechanism to handle the class loader - see http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Class.html#forName(java.lang.String) ; this allows you to decide to use the SAX parser from Apache, simply by making your casting the class loaded by name "org.apache.xerces.parsers.SAXParser" as an XMLReader.

You need to do something a bit more home-grown in C++ to load a class from a .dll or .so - e.g. have the library load a function pointer to its class factory in a global map to register it by name or export the class factory function with a standard name. I've done this myself for some "application launcher" applications, which load classes from dynamically linked libraries (.so / .dll), which are required to have a set of required functions. The class factory function in the launched application library is only able to create its own application, so it has no choice; no need for switch/if conditions. The class itself is selected from a map by name / telephone number / time / the beat of a butterfly's wings in China.

MFC doesn't provide you with this pattern as far as I am aware, but there is nothing to stop you implementing it yourself within an MFC application.
0
 
LVL 30

Assisted Solution

by:Axter
Axter earned 200 total points
ID: 12123564
FYI:
>>The class itself is selected from a map by name / telephone number / time / the beat of a butterfly's wings in
>>China.

That is the registry method, which I referred to in my first post.
However, even this method still has condition logic hidden in the map class.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12123992
> registry method

I warned you that I'm not too hot at design patterns :-) I must confess that I find the differences between some of them are a bit contrived, but my Go4 book is less thumbed than perhaps it ought to be.

> condition logic hidden in the map class

True, but...

> you would not have to use switch/case or if statements to determine what child type to create.

...because the map does it for you. The point being that the calling code does not need to be recompiled for each new registered class loader.
0
 
LVL 39

Accepted Solution

by:
itsmeandnobodyelse earned 150 total points
ID: 12124519
>>>>Does any one here believes that MFC uses class factory method

Yes, in an SDI or MDI application, the document, view and frame objects are created using a factory method. A factory is made by providing a static member function that returns a newly created object of the own class:

   class A : public O
   {
         ....
   public:
         static O* factory() { return new A; }
   };


   class B : public O
   {
         ....
   public:
         static O* factory() { return new B; }
   };

Then you need a factory class where the class names and a function pointer to the static factory function is hold in an array or map (as Axter already told you):

typedef O* (*FactoryFunc)();
struct FactoryAssoc
{
      string          className;
      FactoryFunc factoryFunc;
};

class Factory
{
       private:
             static vector<FactoryAssoc*> factories;
             
       public:
             static registerFactory(const string& className, FactoryFunc factory)
             {
                  factories.push_back(new FactoryAssoc(className, factory));
             }
             
             static O* factory(const string& className)
             {
                  for (int i = 0; i <  factories.size(); i++)
                  {
                       if (factories[i].className == className)
                            return factories[i].factory();
                  }
                  return NULL;
             }
};

Now, the only thing left is to call the Factory::registerFactory member once for any derived class of baseclass O to make it work.

In MFC class O is called Object and the factory functions of your view, document and frame classes are exported by DECLARE_DYNCREATE and IMPLEMENT_DYNCREATE macros (normally generated by Application Wizard). The FactoryAssoc is in CRuntimeClass and the Factory class is implemented by CSingleDocTemplate and the registering happens in the application class when the CSingleDocTemplate* member of your application class is created.

Regards, Alex
0
 

Author Comment

by:Hollaceluna_
ID: 12124741
>>In MFC class O is called Object and the factory functions of your
>>view, document and frame classes are exported by DECLARE_DYNCREATE and

That sounds like it fits the description of a factory method, but it uses macros and I can't see that being a good OO approach.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 12124929
>>>> but it uses macros and I can't see that being a good OO approach

You are right, but the use of macros is not essential as you could see my example above don't uses macros. The purpose of the macros simply is to hide the complexety of the factory method (some macros expand to 10 and more source code lines).

Regards, Alex
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12124988
>  class A : public O
   {
         ....
   public:
         static O* factory() { return new A; }
   };

Does that really count? You call A::factory to create an instance of an A.

Isn't the following more to the point...

   class A : public O
   {
         ....
   public:
         static O* factory();
   };

   class B : public O {};
   O* A::factory() { return new B; }

...where the factory method returns an object derived from O, for which the calling code doesn't have the concrete definition?
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12125007
NB: the factory method needn't of course be a static member of a class derived from O for that.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12125035
From the inside cover of the Go4 Design Patterns book...

Factory Method(107): Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory class lets a class instantiation to subclasses.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 12125239
>>>> Does that really count?

The A::factory isn't the factory, but only a function where the factory class is able to store the function pointer and make an association to a unique class name or a class ID.

>>>> the factory method needn't of course be a static member of a class derived from O

The function must be static as the factory has to call it without having an object of class A.

>>>> Define an interface for creating an object, but ...

That's a strange definition that has less to do with numerous implementations of factory class i've made or seen. The benefits of factory classes you may see with persistent database objects where a DBMS reads some data from a database file and dynamically has to create class objects. That could be done in that way that all 'persistent' classes register their (static) create function in the DBMS class associating a unique ID to that function. That ID was stored with the record data and the DBMS factroy class is able to create (and assign data) the transient class objects as a result of a query.

Regards, Alex
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12125551
> The function must be static as the factory has to call it without having an object of class A.

My point was that it doesn't need to be a static function <EM>of a class derived from O</EM>.

It could be..

  class foo {
  static o* factory();
  };

...or indeed...

  namespace o_factory {
  static o* factory(); // Not even a method
  };

...per that definition.

> That's a strange definition

Once again, I'll make my disclaimer that I'm not too hot at design patterns... I'm looking at a shamefully unthumbed copy of Gamma et al... but that's the definition I see :-)

> persistent database objects

That's a better example than the SAX parser, because it is actually used in the C++ world. In your book, do you differentiate "registry methods" from "factory class methods" like Axter?
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 12125740
>>>> that it doesn't need to be a static function of a class derived from O

The factory doesn't know of class A, class B but of class O.  So, to be able to store a function pointer from A and B in an array or map the function must return the pointer of a common baseclass, e. g. class O. It could return a void pointer, but it's hard to call virtual functions, e. g. loadData(..), if you have no common baseclass.

>>>> shamefully unthumbed copy of Gamma

i am so sorry, but i never heard of these books (more than unthumbed, eh.. unknown). I only read the bible (Stroustrup) and EE comments. I am  a  practitioner not a teacher ;-)

>>>> differentiate "registry methods" from "factory class methods" like Axter

As i unterstood Axter's comments his registry method is the same as my factory method.

Regards, Alex
0
 
LVL 30

Expert Comment

by:Axter
ID: 12126296
>>As i unterstood Axter's comments his registry method is the same as my factory method.

That's correct.
The method you posted, is the common method for a registry class factory method.

There are ways to do it without a static function, but then the container would have to store an actual instance of the object, instead of a pointer to a member function.

Using a static member function is usually the most optimize method.

rstaveley,
You might not realize it, but itsmeandnobodyelse is *not* storing an instance of the child class in the container.
typedef O* (*FactoryFunc)();
struct FactoryAssoc
{
      string          className;
      FactoryFunc factoryFunc;
};

The container is storing  FactoryAssoc, which only has a string, and a pointer to a member function.
In order to use a none-static member function, the container would actually have to store an instance of the child class.
Something like the following:
struct FactoryAssoc
{
      string          className;
      O* ChildClassPtr;
};

Now you would be able to call a virtual function to create a NEW instance of the class using the factory code.
But if your child class has complex data, or a large amount of data members, this will peform badly compared to using static member function method.
0
 
LVL 30

Expert Comment

by:Axter
ID: 12126906
Here's an example for the registry method using static member functions, that my be a little easier to read.

class Shape
{
public:
      typedef Shape* (*FactoryFunc)();
      virtual string WhatShapeAmI() = 0;
      static void RegisterShapeType(const string &Name, FactoryFunc func);
      static Shape* CreateClass(const string &Name){
            return (RegisteredTypes[Name])();
      }
private:
      static map<string, FactoryFunc> RegisteredTypes;
};

class Circle : public Shape
{
public:
      static Shape* CreateInstance(){
            return new Circle();
      }
      string WhatShapeAmI(){ return "I am Circle!";}
};

class Square : public Shape
{
public:
      static Shape* CreateInstance(){
            return new Square();
      }
      string WhatShapeAmI(){ return "I am Square!";}
};

map<string, Shape::FactoryFunc> Shape::RegisteredTypes;
void Shape::RegisterShapeType(const string &Name, FactoryFunc func)
{
      RegisteredTypes[Name] = func;
}

int main(int argc, char* argv[])
{
      Shape::RegisterShapeType("Circle", Circle::CreateInstance);
      Shape::RegisterShapeType("Square", Square::CreateInstance);

      auto_ptr<Shape> s1(Shape::CreateClass("Circle"));
      auto_ptr<Shape> s2(Shape::CreateClass("Square"));

      cout << s1->WhatShapeAmI() << endl;
      cout << s2->WhatShapeAmI() << endl;
      
      return 0;
}

As you can see from the above example, that map container does *not* store an instance of the class, and instead only stores a pointer to a member function.

It is possible to store a pointer to a member function that is not static.
However, if the container stored none-static member functions, then you would have to create an instance of the class, before using the none-static member function.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12126947
Thanks for putting me straight, both of you. I think I've been suffering from attention deficit disorder today. It was very well put by itsmeandnobodyelse, if only I'd taken the time to read it properly.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12126972
...and beautifully illustrated by Axter :-)
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12127004
In the back of my mind though, there ought to be an instance of a static class that does the registrations...

   Shape::RegisterShapeType("Circle", Circle::CreateInstance);
   Shape::RegisterShapeType("Square", Square::CreateInstance);

....which is why I'm not 100% sold on the idea of a factory methos creating instances of itself.
0
Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

 
LVL 30

Expert Comment

by:Axter
ID: 12127021
>>In the back of my mind though, there ought to be an instance of a static class that does the registrations...

What do you mean by a "static class"?
0
 
LVL 30

Expert Comment

by:Axter
ID: 12127044
Here's a modified version of the code I posted, but with this one I'm storing pointers to the child class in the map container.

class Shape
{
public:
      virtual string WhatShapeAmI() = 0;
      static void RegisterShapeType(const string &Name, Shape* s);
      static Shape* CreateClass(const string &Name){
            return RegisteredTypes[Name]->CreateInstance();
      }
private:
      static map<string, Shape*> RegisteredTypes;
      virtual Shape* CreateInstance()=0;
};

class Circle : public Shape
{
public:
      Shape* CreateInstance(){
            return new Circle();
      }
      string WhatShapeAmI(){ return "I am Circle!";}
};

class Square : public Shape
{
public:
      Shape* CreateInstance(){
            return new Square();
      }
      string WhatShapeAmI(){ return "I am Square!";}
};

map<string, Shape*> Shape::RegisteredTypes;
void Shape::RegisterShapeType(const string &Name, Shape* s)
{
      RegisteredTypes[Name] = s;
}

int main(int argc, char* argv[])
{
      Shape::RegisterShapeType("Circle", new Circle());
      Shape::RegisterShapeType("Square", new Square());

      auto_ptr<Shape> s1(Shape::CreateClass("Circle"));
      auto_ptr<Shape> s2(Shape::CreateClass("Square"));

      cout << s1->WhatShapeAmI() << endl;
      cout << s2->WhatShapeAmI() << endl;
      
      //Need to delete map pointers
      return 0;
}

0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12127065
But I guess you can have...

namespace {
  struct registration_helper {
    registration_helper() {
      Shape::RegisterShapeType("Circle", Circle::CreateInstance);
    }
  helper register;
};

...in circle.cpp.
0
 
LVL 30

Expert Comment

by:Axter
ID: 12127071
>>In the back of my mind though, there ought to be an instance of a static class that does the registrations...

To use a static member function, you don't need an instance of the class.
You can call Shape::CreateClass without having an instance of Shape, and infact, it's not possible to have an instance of Shape, since it's an abstract class.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12127088
> What do you mean by a "static class"?

An instance of a helper class in static storage, to do the registration in its ctor. The anonymous namespace is good for this.
0
 
LVL 30

Expert Comment

by:Axter
ID: 12127090
>>...in circle.cpp.

But how would you're class call the registration_helper?

To do that, you would have to create an instance of each child class first.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12127104
That should have read...

namespace {
  struct registration_helper {
    registration_helper() {
      Shape::RegisterShapeType("Circle", Circle::CreateInstance);
    }
  };
  helper register;
}

I'm sure you get the idea.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12127125
> But how would you're class call the registration_helper?

The ctor for registration_helper does the registration. You have a singleton of registration_helper in static storage, which means that registration happens before main() is called.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12127139
Oops. I'll try that again:

namespace {
  struct registration_helper {
    registration_helper() {
      Shape::RegisterShapeType("Circle", Circle::CreateInstance);
    }
  };
  registration_helper register;
}

Hopefully it makes more sense without the typo.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12127196
Once you take the step though of having something like registration_helper to do the registration, it is easy to take the additional step:

namespace {
  struct factory {
    factory() {
      Shape::RegisterShapeType("Circle", CreateInstance);
    }
    static Shape* CreateInstance() {
        new Circle();
    }
  };
  factory hidden_factory;
}

Incidentally, I don't see that CreateInstance has any business being a virtual method. Surely it ought to be a static function.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12127214
>> Incidentally, I don't see that CreateInstance has any business being a virtual method. Surely it ought to be a static function.

> but with this one I'm storing pointers to the child class in the map container

Sorry once again I hadn't read what was posted properly. I see :-)
0
 
LVL 30

Expert Comment

by:Axter
ID: 12127221
>>Hopefully it makes more sense without the typo.

I think I see what you're saying.
I think you're talking about making one class that registers all the objects.
If so, then I would recomend having the abstract class do this job instead.

Example:
class Shape
{
public:
     typedef Shape* (*FactoryFunc)();
     virtual string WhatShapeAmI() = 0;
     static void RegisterShapeType(const string &Name, FactoryFunc func);
     static Shape* CreateClass(const string &Name){
          return (RegisteredTypes[Name])();
     }
private:
     static map<string, FactoryFunc> RegisteredTypes;
       struct registration_helper{
             registration_helper();
       };
       static registration_helper m_registration_helper;
};

class Circle : public Shape
{
public:
     static Shape* CreateInstance(){
          return new Circle();
     }
     string WhatShapeAmI(){ return "I am Circle!";}
};

class Square : public Shape
{
public:
     static Shape* CreateInstance(){
          return new Square();
     }
     string WhatShapeAmI(){ return "I am Square!";}
};

map<string, Shape::FactoryFunc> Shape::RegisteredTypes;
void Shape::RegisterShapeType(const string &Name, FactoryFunc func)
{
     RegisteredTypes[Name] = func;
}

Shape::registration_helper::registration_helper()
{
     Shape::RegisterShapeType("Circle", Circle::CreateInstance);
     Shape::RegisterShapeType("Square", Square::CreateInstance);
}

Shape::registration_helper Shape::m_registration_helper;

int main(int argc, char* argv[])
{

     auto_ptr<Shape> s1(Shape::CreateClass("Circle"));
     auto_ptr<Shape> s2(Shape::CreateClass("Square"));

     cout << s1->WhatShapeAmI() << endl;
     cout << s2->WhatShapeAmI() << endl;
     
     return 0;
}

0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12127267
> If so, then I would recomend having the abstract class do this job instead.

No, because the Shape implementation would need to be aware of all classes derived from Shape which needed to be registered.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12127284
There's something really nice about loading a .dll or .so and having it register its own class in your global map.

e.g. LoadLibrary("circle.dll"), LoadLibrary("square.dll") etc...
0
 
LVL 30

Expert Comment

by:Axter
ID: 12127322
>>No, because the Shape implementation would need to be aware of all classes derived from Shape which >>needed to be registered.

Where ever you put the code, it would have to be aware of the derived shape types.
If you put it in a registration_helper class, then it will have to know about the derived shapes.

You can still put it in a registration_helper, like I did in above example, in which the registration_helper is a sub class of the Shape class.

IMHO, this gives you more encapsulation.
0
 
LVL 30

Expert Comment

by:Axter
ID: 12127342
>>There's something really nice about loading a .dll or .so and having it register its own class in your global map.
>>e.g. LoadLibrary("circle.dll"), LoadLibrary("square.dll") etc...

That's true.
In that case, you wouldn't have much of a choice, other then to use the method you referring to.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12127770
The reason for using the anonymous namespace (or making it static, if you don't like moving with the times ;-) ) is to have the helper only register the class being implemented in the current module.

It is a good contender for one of those evil, evil macros. [Come on admit it, you like them really!]

e.g.

#define REGISTER(name) \
namespace {\
  struct factory {\
    factory() {\
      Shape::RegisterShapeType(#name, CreateInstance);\
    }\
    static Shape* CreateInstance() {\
        new name();\
    }\
  };\
  factory hidden_factory;\
}

..and in circle.cpp have

REGISTER(Circle)

..and in Square.cpp have
REGISTER(Square)

That way each module is responsible for registering itself.
0
 
LVL 30

Expert Comment

by:Axter
ID: 12128124
>>It is a good contender for one of those evil, evil macros.

Yes, but I would make the name much more complicated, so as to reduce the posibility of conflicts.

#define REGISTER_SHAPE_TYPE_(name) \

>>[Come on admit it, you like them really!]

I was a C programmer, long befoe I started on C++, so yes, I do like to use my macros.
But only when I can't accomplish the same thing by any other means.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 12130554
>>[Come on admit it, you like them really!]

Yes, there is some fascinating thing to watch the evil. Look at this class definition:

class BasSystem : public Persistent
{
// @dataprivate
private:
   
   // declare BasSystem as persistent class
   DECL_PERS_TBL(BasSystem, BAS, BASSYSTEM)

   DECL_PERS_FLD_ALL(sysKey, SYSKEYWORD, String, VARCHAR, 50, SCF_PRIMKEY)
   DECL_PERS_FLD_LEN(sysVal, SYSKEYVALUE, String, VARCHAR, 250)

public:
// @funcpublic:
   BasSystem() {}
   virtual ~BasSystem() {}

};

These nice macros give an OO interface to a SQL table. Here an extraction of my macro header file:

// @func Macro | DECL_PERS_TBL | This macro can be used once in the class declaration
// of persistent classes (that are classes (directly or indirectly) derived from
// class Persistent (see below)). DECL_PERS_TBL can NOT be used for base classes
// of classes with persistent fields (use <c DECL_PERS_BAS> instead) i.e. this
// macro is intended for the leaf classes of the class tree where the root
// is the class Persistent.<nl>
// DECL_PERS_TBL defines the class individual interface to a table of a SQL
// database and includes the following code elements:<nl>
// - static member m_tabId which will hold a unique table id for
//   the class<nl>
// - declaration and inline implementation of static member function registerSqlTable...
//   to create the table id and to register the table definitions.
// - declaration of virtual member function doPersistence(..) to handle
//   data exchange between the persistent class members and a SQL record buffer
//   (both directions) and to supply the structure definition of the SQL table.
// - declaration and inline implementation of virtual member function init() to
//   register the class initially (using doPersistence(PACT_INIT,...))<nl>
// - declaration and inline implementation of virtual member function getTabId()
//   to get the table id (and so the access to all registered column info)
// - declaration and inline implementation of virtual member function create() to
//   create new empty instances of the class using one given instance as factory.<nl>
// - declaration and inline implementation of static member function addColumnFlags() for
//   easily adding some field flags (such as SCF_NOT_NULL or SCF_INDEX) to the field
//   definitions.<nl>
// <nl>
// Note: this macro will set the class protection scope to private. Be sure, that your
// scope is set again after this macro if you need a different scope.
//
// @parm ConstCharPtr | cls      | class name
// @parm ConstCharPtr | db       | database name (use capital letters)
// @parm ConstCharPtr | tab      | table name (use capital letters)
#define DECL_PERS_TBL(cls, db, tab) \
public: \
   static Int           m_tabId; \
   static Bool          m_useSynonym; \
   static Int           tabId() { if (m_tabId <= 0) init(); return m_tabId; } \
   static Int           registerSqlTable##cls(String dbName= #db, String tableName= #tab, String className = #cls) \
      { return Persistent::registerSqlTable(dbName, tableName, className, &cls::newObject); } \
   virtual String       getTabName() { return #tab; } \
   virtual String       getSynonym() { return #tab; } \
   virtual String       getSchema()  { return #db;  } \
   virtual PRet         doPersistence(PAct action, String dataName, PEx& ex, Int tabId = -1, Int colNum = -1); \
   virtual Int          getTabId() { if (m_tabId <= 0) init(); return m_tabId; } \
   virtual Persistent*  create(DerivedId dId = 0) { return new cls; } \
   static void          init(void) { cls any; PEx ex; any.doPersistence( PACT_INIT, "", ex); } \
   static void          init(PEx& ex) { cls any; any.doPersistence( PACT_INIT, "", ex); } \
   static UInt          addColumnFlags(String name, UInt flags) \
      { return Persistent::setColumnFlags(tabId(), name, flags, True); } \
   static cls*          getObject(String key, Int connectId = -1, Bool bFull = False); \
   static Int           loadAll(Int connectId = -1, Bool bFull = False) ; \
   static Bool          createTable(Bool& bAlreadyExists, Int connectId = -1) ; \
   static Bool          dropTable(Bool& bAlreadyExists, Int connectId = -1, String tableOld = "") ; \
   static Int           countObjects(Int connectId = -1); \
   static Persistent*   newObject(Int tabId = -1) { return new cls; } \
private:


// @func Macro | IMPL_PERS_TBL_BEGIN | This macro must be added to the implementation  
// (cpp-file) of a persistent class for which the macro DECL_PERS_TBL had been used.
// It defines the static member variable m_tabId declared in DECL_PERS_TBL and contains
// the begin of the implementation of the member function doPersistence also declared
// by the related declaration macro. The implementation of doPersistence() will be
// accomplished using the IMPL_PERS_FLD... macros (defined below) for each member variable
// declared by one of the DECL_PERS_FLD.. macros. The IMPL_PERS_FLD... macros must
// be included immediately after the IMPL_PERS_TBL_BEGIN macro because they are adding
// statements to the body of the doPersistence function. The sequence must be closed
// using the IMPL_PERS_TBL_END macro defined below
//
// @parm ConstCharPtr | cls  | class name of the persistent class
// @parm ConstCharPtr | bas  | class name of the base class (take Persistent
//                             if no persistent base class is between)
#define IMPL_PERS_TBL_BEGIN(cls, bas) \
Int  cls::m_tabId   = -1; \
Bool cls::m_useSynonym = False; \
cls* cls::getObject(String key, Int connectId, Bool bFull) \
{ \
   if (m_tabId <= 0) init(); \
   cls* p = (cls*)bas::getObject(m_tabId, key, connectId); \
   if (p != NULL || connectId == 0) return p; \
   cls* pFactory = new cls; \
   p = (cls*)pFactory->bas::loadObject(key, connectId, bFull, True); \
   if (p != pFactory) delete pFactory; \
   return p; \
} \
Int cls::countObjects(Int connectId) \
{ \
   return bas::countObjects(tabId(), connectId); \
}  \
Int cls::loadAll(Int connectId, Bool bFull) \
{ \
   if (m_tabId <= 0) init(); \
   cls any; \
   return Persistent::loadArray(connectId, &any, NULL, bFull); \
} \
Bool cls::createTable(Bool& bAlreadyExist, Int connectId) \
{ \
   if (m_tabId <= 0) init(); \
   cls any; \
   return Persistent::createTable(bAlreadyExist, &any, connectId); \
} \
Bool cls::dropTable(Bool& bNotExists, Int connectId, String tableOld) \
{ \
   if (m_tabId <= 0) init(); \
   cls any; \
   return Persistent::dropTable(bNotExists, &any, connectId, tableOld); \
} \
PRet cls::doPersistence(PAct action, String dataName, PEx& ex, Int tabId, Int colNum) \
{ \
   Int  sortOffset  = getSortOffset() - bas::getSortOffset(); \
   Bool bTable      = colNum < 0 && dataName.isEmpty(); \
   if (m_tabId <= 0) \
      m_tabId = registerSqlTable##cls(); \
   else if (bTable && ex.getFieldCount() > 0) \
      {  ex.m_id = m_tabId; return PRET_TRUE; } \
   tabId = m_tabId; \
   if (bTable && action == PACT_INIT) \
      ex.m_colArr.clear(); \
   ex.setFieldCounter(0); \
   PRet ret; \
   if ((ret = bas::doPersistence(action, dataName, ex, tabId, colNum)) <= PRET_DONE) \
      return ret; \
   if (bTable) \
      ex.m_id = m_tabId;

// @func Macro | IMPL_PERS_TBL_END | This macro must be added to the implementation  
// (cpp-file) of a persistent class for which IMPL_PERS_TBL_BEGIN had been used.
// This macro closes the implementation
#define IMPL_PERS_TBL_END \
      return bTable? PRET_TRUE : PRET_FALSE; \
}
                                                                             

Oh, i'm so ashamed... but it runs very well ...

Regards, Alex




0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12130830
Shame on you Alex!

[Thanks for sharing that. It's rather nice actually]
0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Suggested Solutions

Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
Container Orchestration platforms empower organizations to scale their apps at an exceptional rate. This is the reason numerous innovation-driven companies are moving apps to an appropriated datacenter wide platform that empowers them to scale at a …
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now