1

I've used C++ for a while and one thing which occasionally bugs me is that I haven't yet been able to figure out how to do this Delphi Factory construct in C++.

The key part that I can't figure out is how to pass a reference to a class type in C++. In Delphi, we have "TClass" type. Variables of this type are a reference to some class. We can restrict the classes that a class reference can refer to using the class of MyClass syntax to define a new class reference type.

Note that the term "Delphi class reference" =/= "C++ instance of a class"

For those unfamiliar with Pascal, variables are declared variable: type rather than the C-style type variable. Similarly, a function's return type appears after the parameter list and name.

I've condensed the syntax in the following example to make it less boilerplate-y, so apologies to Delphi devs for the terrible formatting. The key parts are described in comments.

program demo;

{$APPTYPE CONSOLE}

// declarations

type

  Base = class
  public constructor Create(name: string); virtual;
  end;

  // Class reference which refers to Base and its descendants
  BaseClass = class of Base;

  ChildA = class(Base)
  public constructor Create(name: string); override;
  end;

  ChildB = class(Base)
  public constructor Create(name: string); override;
  end;

// implementation

constructor Base.Create(name: string);
begin
  WriteLn('Base says hi to ' + name);
end;

constructor ChildA.Create(name: string);
begin
  inherited Create(name);
  WriteLn('ChildA says hi to ' + name);
end;

constructor ChildB.Create(name: string);
begin
  WriteLn('ChildB says hi to ' + name);
end;

// *** THIS IS THE BIT THAT I'M INTERESTED IN ***
// The function doesn't know at compile time exactly what class it is being
// asked to construct.  The compiler knows that it is or inherits from Base.
// I can't find any kind of "class reference" in C++.
function ConstructSomething(ClassType: BaseClass; name: string): Base;
begin
  Result := ClassType.Create(name);
end;

// Equivalent to "main()" in C
begin

  // Pass references to a class to the ConstructSomething function
  ConstructSomething(Base, 'Mark');
  WriteLn('');

  ConstructSomething(ChildA, 'Mark');
  WriteLn('');

  ConstructSomething(ChildB, 'Mark');
  WriteLn('');

end.

Output:

Base says hi to Mark

Base says hi to Mark
ChildA says hi to Mark

ChildB says hi to Mark

Note that when a reference to a child class is passed then the child class is created. Not calling the base constructor in ChildB is intentional in this demo, to make the "class reference" concept slightly more obvious.

Mark K Cowan
  • 1,755
  • 1
  • 20
  • 28
  • I think maybe this question has way too much noise and not enough precision. Also, giving an example written in a language 99% of C++ devs don't know is not going to get you far. A precise question would leave Delphi out of it. "How do I write a function `Factory(ClassRef AClass, string name)` so that I can pass in a class and have it get used in a new-object creation?" – Warren P Aug 15 '14 at 19:34
  • 3
    And the short answer is "you don't do it like this in C++ at all because C++ is a weak pathetic language compared to Pascal." – Warren P Aug 15 '14 at 19:37
  • @WarrenP: The question has a minimal example of the concept that I'm trying to translate. I'm looking for the 1% of C++ devs who do know the language. If I knew the C++ form of it already then I wouldn't need to ask. Does "ClassRef" have any significance in C++ docs? – Mark K Cowan Aug 15 '14 at 20:30
  • And this isn't supposed to be a Pascal vs C++ war, I prefer C++ over Pascal massively. Especially now that C++11 brings in some of my favourite JavaScript features to the language. – Mark K Cowan Aug 15 '14 at 20:30
  • The possible duplicate is a much clearer discussion. I don't care if you like C++ or hate it. (I'm one of the latter, but I respect the former) I do care if your question is clear and useful. It's not. – Warren P Aug 15 '14 at 20:51
  • The linked question has a nice idea for you; Exemplar pattern in C++. – Warren P Aug 15 '14 at 20:53

2 Answers2

5

The closest you can get is using a template function

template<typename ClassType>
Base* ConstructSomething(string name)
{
   return new ClassType(name);
}

int main()
{
    ConstructSomething<Base>("Mark");
    ConstructSomething<ChildA>("Mark");
}

but ClassType can't be selected at runtime - it must be known during compilation.

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
  • The linked duplicate suggests exemplars which are much more flexible than using C++ templates. This doesn't really meet the OP's requirements as he wants to not have to have declared all his template types at compile time. C++ has no such thing as "FULL runtime type information"; its RTTI facilities are laughable, and has no metaclass facilities, that's why I said it was "lame". Personally I would consider using COM and registering my own in-house RTTI/metaclass facilities, if I needed to do this in C++ – Warren P Aug 15 '14 at 20:55
1

C++ has nothing like Delphi's class references built in. And class references are way more than just virtual constructors; you can also define (virtual and non-virtual) class methods, which is very convenient.

You can imitate class references by using C++ templates, but it comes with some boilerplate code, and you have to define separate classes for class and metaclass. Not to mention that this is a rather unidiomatic way of doing C++.

// some central header

template <typename Base>
  class Metaclass;


// your header

class Base
{
public:
  Base (const String& name) { /*...*/ }
};
template<>
  class Metaclass<Base>
{
public:
  virtual Base* create (const String& name);
};
extern Metaclass<Base> classof_Base;

class ChildA : public Base
{
public:
  ChildA (const String& name) { /*...*/ }
};
template<>
  class Metaclass<ChildA> : public Metaclass<Base>
{
public:
  // note how I'm making use of C++'s support for covariance
  ChildA* create (const String& name) override;
};
extern Metaclass<ChildA> classof_ChildA;

class ChildB : public MyBase
{
public:
  ChildB (const String& name) { /*...*/ }
};
template<>
  class Metaclass<ChildB> : public Metaclass<Base>
{
public:
  ChildB* create (const String& name) override;
};
extern Metaclass<ChildB> classof_ChildB;


// your source file

Metaclass<Base> classof_Base;
Base* Metaclass<Base>::create (const String& name)
{
  return new Base (name);
}

Metaclass<ChildA> classof_ChildA;
MyBase* Metaclass<ChildA>::create (const String& name)
{
  return new ChildA (name);
}

Metaclass<ChildB> classof_ChildB;
MyBase* Metaclass<ChildB>::create (const String& name)
{
  return new ChildB (name);
}

Now you can replicate your Delphi code in C++:

Base* constructSomething (Metaclass<Base>& classType, const String& name)
{
  return classType.create (name);
}

int main (void)
{
  constructSomething (classof_Base, "Mark");
  std::sprintf ("\n");
  constructSomething (classof_ChildA, "Mark");
  std::sprintf ("\n");
  constructSomething (classof_ChildB, "Mark");
  std::sprintf ("\n");
}
Moritz Beutel
  • 1,931
  • 2
  • 15
  • 18