0

Components often have long lists of properties with usable default values:

class PACKAGE TMySpecialComboBox : public TCustomComboBox
{
public:
  __fastcall TMySpecialComboBox(TComponent *Owner);
  // ...

private:
  // ...
  bool fetch_all_;
  bool rename_;
  TColor background1_, background2_, background3_;
  // ...

__published:
  // ...
  __property bool FetchAll = {read = fetch_all_, write = fetch_all_,
                              default = false};
  __property bool Rename = {read = rename_, write = rename_,
                            default = false};
  __property TColor Background1 = {read = background1_, write = background1_,
                                   default = clWindow};
  __property TColor Background2 = {read = background2_, write = background2_,
                                   default = clWindow};
  __property TColor Background3 = {read = background3_, write = background3_,
                                   default = clWindow};
  // ...
};

Storing all this information in the form file wastes space and reading it back takes time, which is not desirable, considering that in most cases, few of the defaults change.

To minimize the volume of data in the form file, you can specify a default value for each property (when writing to the form file, the form editor skips any properties whose values haven't been changed).

Note that doing so doesn't set the default value:

Note: Property values are not automatically initialized to the default value. That is, the default directive controls only when property values are saved to the form file, but not the initial value of the property on a newly created instance.

the constructor is responsible for doing that:

__fastcall TMySpecialComboBox::TMySpecialComboBox(TComponent* Owner)
  : TCustomComboBox(Owner), // ...
{
  FetchAll = false;       // how to get the default value ?
  Rename = false;         // how to get the default value ?
  Background1 = clWindow  // how to get the default value ?
  // ...
}

but writing initialization in this way is very error prone.

How can I get the default value of a __property?

manlio
  • 18,345
  • 14
  • 76
  • 126
  • 2
    It is wasted overhead to use RTTI to initialize the property fields like you are asking for. You are coding the component, you know what default values you specify in the `__property` declarations, so just hard-code the same values in the constructor initializations, it is really not that hard. But, if you really want to reduce duplicate effort, just use constants, eg: `const bool cFetchAllDefault = false; ... __property bool FetchAll = {..., default = cFetchAllDefault}; ... TMySpecialComboBox(TComponent* Owner) : TCustomComboBox(Owner) { FetchAll = cFetchAllDefault; ... }` – Remy Lebeau Apr 11 '19 at 22:54
  • @Remy You're absolutely right. The question could probably be classified as a XY problem. Anyway there aren't many examples available about this topic and I hope the answer could be useful for different use cases. – manlio Apr 12 '19 at 06:13

1 Answers1

0

It can be done via the TRttiContext structure.

#include <Rtti.hpp>

int get_property_default(const String &name)
{
  TRttiContext ctx;

  auto *p(ctx.GetType(__classid(TMySpecialComboBox))->GetProperty(name));
  assert(dynamic_cast<TRttiInstanceProperty *>(p));

  return static_cast<TRttiInstanceProperty *>(p)->Default;
}

__fastcall TMySpecialComboBox::TMySpecialComboBox(TComponent* Owner)
  : TCustomComboBox(Owner), // ...
{
  FetchAll = get_property_default("FetchAll");
  // ...
}

References:

manlio
  • 18,345
  • 14
  • 76
  • 126
  • 1
    Prior to Delphi/C++Builder 2010, the default value can be gotten from the [`TPropInfo::Default`](http://docwiki.embarcadero.com/Libraries/en/System.TypInfo.TPropInfo) field from [`GetPropInfo()`](http://docwiki.embarcadero.com/Libraries/en/System.TypInfo.GetPropInfo) (this works only for `__published` properties, though): `FetchAll = GetPropInfo(this->ClassType(), "FetchAll")->Default;` – Remy Lebeau Apr 11 '19 at 22:49