RAD Studio |  |

Class Methods
How Class Methods Work in Delphi | Class Methods as Static Methods with Explicit TMetaClass* Parameter | Class Methods Using __classmethod Keyword | See Also
Collapse All

In C++Builder, a class method is a method that can be invoked on a class name, as well as on an instance of that class. In contrast, object methods can be invoked only on objects -- instances of a class.

How Class Methods Work in Delphi

The Delphi language (Object Pascal) supports class methods and a metaclass as described in the Class Methods section in the Delphi help topic Methods.

Here is a sample:

Copy Code

    TTest = class;

    // Declaring metaclass type
    TTestClass = class of TTest;

    TTest = class
    public
      // Class methods
      class function add(I, J: Integer): Integer;
      class function GetClsName: string;

      // Virtual class method
      class function GetClsNameVirt: string; virtual;

      // Class getter and setter
      class function GetCount: Integer;
      class procedure SetCount(I: Integer);

      // Virtual class getter and setter
      class function GetStrProp: string; virtual;
      class procedure SetStrProp(N: string); virtual;

      // Class static
      class function GetStaticCount: Integer; static;
      class procedure SetStaticCount(I: Integer); static;

      // Class properties
      property Count: Integer read GetCount write SetCount; // Non-virtual
      property StrProp: string read GetStrProp write SetStrProp; // Virtual g/setters
    end;

    // Function that takes a class reference
    function RegisterTestType(Cls: TTestClass): boolean;

Class Methods as Static Methods with Explicit TMetaClass* Parameter

Prior to C++Builder 2009, class methods were represented as static methods with an explicit metaclass parameter. A metaclass is represented by a pointer to a TMetaClass instance or a TClass. This metaclass or class reference is obtained with the __classid extension, which returns a TMetaClass* instance for a class name.

Here is an example of this usage, attempting to define the same methods and properties as in the Delphi sample above. Note that some of the Delphi class method features cannot be done properly using this metaclass approach.

Copy Code

    // All metaclass types are TMetaClass* in C++
    typedef TMetaClass* TTestClass;

    class DELPHICLASS TTest;
    class PASCALIMPLEMENTATION TTest : public System::TObject
    {
      typedef System::TObject inherited;
      public:
          // Class methods exposed as static methods with the 'hidden'
          // class reference explicit as the first parameter.
          static int __fastcall add(TMetaClass* vmt, int I, int J);
          static UnicodeString __fastcall GetClsName(TMetaClass* vmt);

          // Virtual class methods would be exposed as plain virtual methods with
          // the hidden class reference explicit as the first parameter.
          // This means that upon calling this method from C++, there would have
          // to be two 'hidden' parameters passed in--which would not work.
          virtual UnicodeString __fastcall GetClsNameVirt(TMetaClass* vmt);

          // Non-virtual methods are feasible to work with. These two methods
          // are typically overloaded with the first TMetaClass* parameter
          // hardcoded to __classid(TTest).
          static int __fastcall GetCount(TMetaClass* vmt);
          static void __fastcall SetCount(TMetaClass* vmt, int I);

          // You can overload these virtual setters and getters, but given
          // that the call is incorrect, the program fails
          // when accessing the property tied to these methods.
          virtual UnicodeString __fastcall GetStrProp(TMetaClass* vmt);
          virtual void __fastcall SetStrProp(TMetaClass* vmt, UnicodeString N);

          // Delphi class static method would be plain C++ static.
          static int __fastcall GetStaticCount();
          static void __fastcall SetStaticCount(int I);

          // Although the compiler allows these declarations,
          // because TMetaClass* is required, you'll get an error
          // from the C++ compiler upon attempting to access these properties.
          __property int Count = {read=GetCount, write=SetCount, nodefault};
          __property UnicodeString StrProp = {read=GetStrProp, write=SetStrProp};};

    extern PACKAGE bool __fastcall RegisterTestType(TMetaClass* Cls);

There are several limitations with this way of exposing class methods using an explicit TMetaClass* parameter:

- C++ code cannot properly invoke virtual class methods. C++ calls would have to pass two hidden this parameters: the pointer to the instance of the object and the explicit TMetaClass* parameter. However, the function expects only one TMetaClass* parameter.
- Even in cases where the call to a virtual class method succeeds, the C++ code must have an instance of the object to invoke the method, but a proper class method should be invokable without requiring an object instance.
- C++ code cannot properly access properties whose getters or setters are class methods, because there's no way to provide the TMetaClass* parameter, which must be explicit.

Class Methods Using __classmethod Keyword

C++Builder 2009 introduces class methods that remove the limitations listed above and provide a simpler and more intuitive syntax for class methods. Class methods are now declared with the new keyword __classmethod.

Here is how the code above is written using the __classmethod syntax:

Copy Code

    // Class references still use TMetaClass*.
    typedef TMetaClass* TTestClass;

    class DELPHICLASS TTest;
    class PASCALIMPLEMENTATION TTest : public System::TObject
    {
      typedef System::TObject inherited;

      public:
          // The TMetaClass* parameter is now hidden.
          // The __classmethod keyword flags methods as class methods.
          __classmethod int __fastcall add(int I, int J);
          __classmethod UnicodeString __fastcall GetClsName();

          // Virtual methods can be used.
          __classmethod virtual UnicodeString __fastcall GetClsNameVirt();

          // Methods can access class properties
          __classmethod int __fastcall GetCount();
          __classmethod void __fastcall SetCount(int I);
          __classmethod virtual UnicodeString __fastcall GetStrProp();
          __classmethod virtual void __fastcall SetStrProp(UnicodeString N);

          // Class static methods still map to C++ static methods.
          static int __fastcall GetstaticCount();
          static void __fastcall SetstaticCount(int I);

          // Class properties
          __property int Count = {read=GetCount, write=SetCount, nodefault};
          __property UnicodeString StrProp = {read=GetStrProp, write=SetStrProp};
    };

C++Builder does not provide for a way to declare a class reference other than by TMetaClass*. Hence, class methods are invoked with a typename or with an instance of a object. Virtual class methods are invoked the same way regular virtual methods are invoked, except that instead of the this pointer being pushed, the metaclass pointer is the hidden parameter.

This implementation provides two capabilities:

- Class methods can be virtual.
- Class properties can be defined.

Dynamic Dispatch of Virtual Class Methods

If you have a class method defined with __classmethod for a class that is derived from another class, you can't dynamically invoke the derived class's virtual method using a class name. However, you can invoke the proper method using an instance.

The "virtual" mechanism of class methods is analogous to virtual for regular methods. For regular methods, you get polymorphism only when using a pointer or a reference, because the vtable is then determined at run time. With a value instance, you don't get polymorphism because the vtable is determine at compile time.

Similarly, for class methods you get polymorphism with an instance but not with a type. To illustrate:

Copy Code

    class TBase {
        virtual __classmethod void cfunc();
        virtual  void vfunc();
    };

    class TDerived: TBase {
        virtual __classmethod void cfunc();
        virtual void vfunc();
    };

    // Regular virtual methods
    TDerived d;
    d.vfunc();   //calls TDerived::vfunc;

    TBase* bp = new TDerived();
    bp->vfunc(); //calls TDerived::vfunc;

    TBase& br = TDerived();
    br.vfunc();  //calls TDerived::vfunc;

    TBase b;
    b.vfunc();   //calls TBase::vfunc
    // Class virtual methods
    TBase* b = new TDerived();
    b->cfunc();         //calls version in TDerived--dynamic
    TDerived::cfunc();  //calls version in TDerived--compile time--not dynamic
    __classid(TDerived)->cfunc();  //compiler error

Updating Your Code to Use __classmethod

If you have C++ code that implements class methods using the older style class methods with a Metaclass * parameter, you can update your code to use the __classmethod syntax. Since the TMetaClass* parameter is now hidden, the signature and mangling of such class methods are different. The following table illustrates how to update common C++ constructs using class methods. It uses the add(int i, int j) method and the Count property from the __classmethod code sample above to illustrate the changes you would make.

Purpose of code | Old style “class static” code | Updated code using __classmethod
Function declaration | class TTest : public System::TObject { public: static int __fastcall add(TMetaClass* vmt, int I, int J); }; | class TTest : public System::TObject { public: __classmethod int __fastcall add(int I, int J); };
Using __classid | TTest::add(__classid(TTest), 10, 20); | TTest::add(10, 20);
Using derived __classid | TTestDerived::add(__classid(TTestDerived), 10, 20); | TTestDerived::add(10, 20);
Using class instance | TTest* p = new TTest(); ... TTest::add(p->ClassType(), 10, 20); | TTest* p = new TTest(); ... p->add(10, 20);
Using derived instance | TTest* p = new TTestDerived(); ... TTest::add(p->ClassType(), 10, 20); | TTest* p = new TTestDerived(); ... p->add(10, 20);
Using class property | TTest* p = new TTest(); ... p->Count = 20; | TTest::Count = 20; //Using class name // Using instance TTest* p = new TTest(); ... p->Count = 20;

See Also

C++ Classes

Class Types

Class Scope

Class Member List

Member Functions

The Keyword This

Static Members

Inline Functions

Member Scope

Nested Types

Member Access Control

Base And Derived Class Access

Virtual Base Classes

Friends Of Classes

Copyright(C) 2009 Embarcadero Technologies, Inc. All Rights Reserved.
What do you think about this topic? Send feedback!