Шаблоны C ++ хорошо играют с классами VCL? - PullRequest
3 голосов
/ 21 декабря 2010

Я пытаюсь использовать шаблоны C ++ 'mixins' для создания некоторых новых компонентов VCL с общей дополнительной функциональностью.Пример ...

template <class T> class Mixin : public T
{
private:
  typedef T inherited;

// ...additional methods

public:
  Mixin(TComponent *owner) : inherited(owner)
  {
  // .. do stuff here
  };
};

Используется примерно так:

class MyLabel : public Mixin<TLabel>
{
  ....
}

class MyEdit : public Mixin<TEdit>
{
  ....
}

Теперь все отлично компилируется, и, похоже, работает миксин - пока я не попытаюсь сохранить компонент в потокеиспользуя TStream-> WriteComponent, где унаследованные свойства (например, TLabel.Width / Height / и т.д.) не записываются.Это даже при использовании 'нулевого' миксина, подобного показанному выше.

Мой код работает нормально, просто производя классы непосредственно из TForm, TEdit и т. Д., И класс правильно зарегистрирован в потоковой системе.

1 Ответ

8 голосов
/ 21 декабря 2010

Быстрый / простой ответ: нет;при работе с шаблоном компилятор не будет генерировать правильные дескрипторы для обеспечения работы потоковой передачи.Тем не менее, так как это произошло раньше, я заглянул под крышку, чтобы выяснить, чего не хватает.И я обнаружил, что это почти там.Итак, вот немного больше информации.

В начале работы компилятор никогда не будет рассматривать тип на основе шаблона как Delphi.Например, сделайте что-то вроде этого:

void testing()
{
  __classid(Mixin<Stdctrls::TLabel>); // Error Here
}

... и вы увидите ошибку

" Ошибка E2242 test.cpp 53: __classid требует Delphiтип класса стиля (т. е. класс, помеченный __declspec (delphiclass) или производный от System :: TObject) в функции testing () "

В основном это говорит о том, что компилятор не считает этот тип / класс совместимым с Delphi-классами [то есть те, которые являются производными от TObject].Внутренне на символе есть только флаг, который говорит, является ли тип delphi-совместимым или нет.И я заметил, что мог бы обмануть компилятор, чтобы он помечал тип как стиль delphi, если бы заставлял его идти вверх по иерархии ... это то, что он должен делать, если я создаю экземпляр объекта.Итак, с этим хаком ошибка исчезает:

void testing()
{
  typedef Mixin<Stdctrls::TLabel> __ttype;
  std::auto_ptr<__ttype> c2(new __ttype(0));
  __classid(Mixin<Stdctrls::TLabel>); // No more errors here
}

Но гораздо приятнее было на самом деле использовать __declspec (delphiclass) непосредственно в шаблоне, как в:

template <class T> 
class __declspec(delphiclass) Mixin : public T {
private:
  int i;
  typedef T inherited;
public:
  __fastcall Mixin(TComponent *owner) : inherited(owner) {};
};

Так что теперьчто компилятор рассматривает тип как класс в стиле delphi без хаков, я немного заглянул и обнаружил проблему, с которой вы, вероятно, сталкиваетесь: классы Delphi имеют поле TTypeData.PropCount - http://docwiki.embarcadero.com/VCL/en/TypInfo.TTypeData - которое являетсясумма свойств класса, включая свойства его базовых классов.Из-за того, как различные части информации вычисляются, компилятор выдает «0» для этого поля, когда задействован шаблон: (

Это можно увидеть, распечатав PropCount, как в:

#include <Stdctrls.hpp>
#include <cstdio>
#include <memory>
#include <utilcls.h>

class TCppComp : public Classes::TComponent {
  int i;
public:
  __fastcall TCppComp(TComponent* owner): Classes::TComponent(owner) {};
__published:
  __property int AAAA = {read=i, write=i};
};

template <class T> 
class __declspec(delphiclass) Mixin : public T {
private:
  int i;
  typedef T inherited;
public:
  __fastcall Mixin(TComponent *owner) : inherited(owner) {};
};

typedef Mixin<TCppComp> TMixinComp;

void showProps(TClass meta) {
  PTypeInfo pInfo = PTypeInfo(meta->ClassInfo());
  int Count = GetPropList(pInfo, tkAny, NULL);
  TAPtr<PPropInfo> List(new PPropInfo[Count]);
  std::printf("Class: %s - Total Props:%d\n", 
                   AnsiString(pInfo->Name).c_str(), Count);  
  GetPropList(pInfo, tkAny, *(reinterpret_cast<PPropList*>(&List)));
  for (int i = 0; i < Count; i++) {
    AnsiString propName(List[i]->Name);
    std::printf("\t%s\n", propName.c_str());
  }
}

void test() {
  showProps(__classid(TCppComp));
  showProps(__classid(TMixinComp));
}

int main() {
  test();
  return 0;
}

При запуске указанных выше отпечатков:

  Class: TCppComp - Total Props:3
    AAAA
    Name
    Tag
  Class: @%Mixin$8TCppComp% - Total Props:0

IOW, Mixin обнаруживается с опубликованными свойствами '0', в то время как его базовый тип имеет 3: (*

Я подозреваюсистема потоковой передачи полагается на этот счетчик, и поэтому унаследованные свойства не записываются в вашу настройку.

Я рассмотрел настройку сгенерированных дескрипторов во время выполнения, но, поскольку мы записываем их в _TEXT, он обязательно вызывает DEP.

Я посмотрю на логику, которая вычисляет PropCount, чтобы увидеть, есть ли какой-нибудь способ заставить его вычислить правильное число. Если время позволяет, пожалуйста, откройте QC для этого: теперь, когда я заглянул внизу,Я полагаю, что это не потребовало бы больших усилий, чтобы заставить это работать как ожидалось.

Приветствия,

Бруно

PS: в моем образце у меня даже была публикация Mixinsh свойство и компилятор сгенерировал правильный дескриптор для этого свойства;тем не менее, общее количество было по-прежнему нулевым.

...