Генерація вихідного коду компонента додаток перегляду класів у CBuilder

Коли користувач натискає кнопку OK сторінкового діалогу, він має на увазі, що додаток у відповідь на це згенерує йому код для нового компонента Очевидно, що спочатку програма повинна провести деякі перевірки (наприклад, перевірити, чи введені імена вихідного і заголовного файлу), але в основному вона буде залишати введені вами значення на вашій совісті, не намагаючись їх виправляти Якщо, наприклад, ви задали неприпустимі імена для властивостей, програма дозволить згенерувати ці властивості Це неминуча розплата за обсяг роботи з написання додатки – чим більше в програмі перевірок, тим важче її написати, а головне, налагодити Природно, ніхто не збирається обмежувати свободу вашої творчості – якщо ви захочете зробити програму більш суворої – будь ласка Якщо ви зможете довести її до пуття,

дайте мені знати – я з великим інтересом подивлюся на результат

Отже, додайте обробник для кнопки OK додайте в нього наступний код: void __ fastcall TPagesDlg :: OKBtnClick (TObject * Sender)

{

/ / Перевіряємо введену інформацію на повноту

if ( ClassName-&gtTextLength() == 0 )

{

MessageBox (NULL, Необхідно вказати імя класу”, Error, MB_OK) return

}

if ( SourceFile-&gtTextLength() == 0 )

{

MessageBox (NULL, Необхідно задати імя вихідного файлу”, Error, MB_OK) return

}

if ( HeaderFile-&gtTextLength() == 0 )

{

MessageBox (NULL, Необхідно задати імя заголовного файлу”, Error, MB_OK) return

}

/ / Створюємо результуючий вихідний файл FILE * fp = fopen (HeaderFile-> Textc_str (), w) if (fp == NULL)

{

MessageBox (NULL, Не можу відкрити заголовний файл”, Error, MB_OK)

return

}

GenerateHeaderFile(fp) fclose(fp)

FILE *sfp = fopen(SourceFile-&gtTextc_str(), &quotw&quot) if ( sfp == NULL )

{

MessageBox (NULL, Не можу відкрити вихідний файл”, Error, MB_OK)

return

}

GenerateSourceFile(sfp) fclose(sfp)

}

Цей код сам по собі особливо не примітний, але на його прикладі я хотів би дати вам кілька порад По-перше, з цього коду видно, як можна перервати процес генерації коду для компонента, видавши повідомлення про помилку (настійно рекомендую) і повернувши управління з обробника По-друге, в цьому коді використовуються старомодні функції введення-виведення fopen і fprintf Так от, ці функції чудово працюють в CBuilder, так що не бійтеся їх використовувати Ви, звичайно, цілком можете працювати і з потоками введення / виводу (stream), але в даному додатку широко використовується форматований висновок – слабке місце цих потоків

Сама функція перевіряє, чи введені всі необхідні для генерації поля (імя класу, базовий клас, заголовний файл) Якщо це зроблено, програма намагається відкрити заголовний файл для запису Хочу відзначити, що це рішення зовсім не є очевидним В даному випадку я волів цілком переписувати при генерації вихідний і заголовний файли, хоча більш перспективним на перший погляд варіантом було б додавання коду в існуючий файл Однак у цьому випадку нас підстерігає куди більше проблем, ніж видно з першого погляду, особливо це стосується заголовного файлу Доведеться турбуватися про всі підключаються файлах, особливо про файли базового класу, і не тільки про це Щоб уникнути всіх цих проблем, я прийняв більш простий варіант – щоразу створювати нові файли Власне, ця проблема не відноситься безпосередньо до основної теми нашої розмови мені просто хотілося звернути вашу увагу на один з підводних каменів, що підстерігають вас на шляху розробника компонентів і Майстрів, і продемонструвати необхідність мислити глобальними категоріями, а не конкретними потребами окремої програми

Ця функція використовує декілька допоміжних функцій Спочатку давайте розглянемо генерацію заголовного файлу У заголовному файлі класу компонента міститимуться тільки описи і ніякого коду Нижче приведений код функції GenerateHeaderFile, Яку треба додати в наш додаток (сподіваюся, ви не забуваєте додавати прототипи функцій в заголовний файл):

void TPagesDlg::GenerateHeaderFile(FILE *fp)

{

fprintf(fp,&quot//——————————————————-   ——————-\n&quot)

fprintf(fp,&quot#ifndef %sH\n&quot, ClassName-&gtTextc_str() ) fprintf(fp,&quot#define %sH\n&quot, ClassName-&gtTextc_str() )

fprintf(fp,&quot//——————————————————-   ——————–\n&quot)

fprintf(fp,&quot#include  &ltvcl\SysUtilshpp&gt\n&quot) fprintf(fp,&quot#include  &ltvcl\Controlshpp&gt\n&quot) fprintf(fp,&quot#include  &ltvcl\Classeshpp&gt\n&quot) fprintf(fp,&quot#include  &ltvcl\Formshpp&gt\n&quot)

fprintf(fp,&quot//——————————————————-   ——————–\n&quot)

fprintf(fp,&quotclass %s : public %s\n&quot, ClassName-&gtTextc_str(), ComboBox1-&gtTextc_str() ) fprintf(fp,&quot{\n&quot) fprintf(fp,&quotprivate:\n&quot) GenerateMemberVariables(fp) fprintf(fp,&quotprotected:\n&quot) fprintf(fp,&quotpublic:\n&quot)

for ( int i=0 i&ltListBox2-&gtItems-&gtCount ++i )

{

fprintf(fp, &quot %s\n&quot,

ListBox2-&gtItems-&gtStrings[i]c_str()  )

}

fprintf(fp,&quot__published:\n&quot) GenerateProperties(fp) fprintf(fp,&quot}\n&quot)

fprintf(fp,&quot//——————————————————-   ——————–\n&quot)

fprintf(fp,&quot#endif\n&quot)

}

Нічим не примітна функція, чи не так Для генерації заголовного файлу нам потрібна деяка основа У заголовному файлі завжди міститься пара ifdef / endif, яка

запобігає неодноразове включення заголовного файлу для використання VCL включаються заголовні файли базового класу Після цього йде власне опис класу Воно складається з виразу, що визначає клас, фігурних дужок, секцій private, public і protected Ми дісталися до нашої першої проблеми – генерації змінних-членів класу в секції private Кожна властивість, визначене в класі, повинно мати відповідну йому змінну класу для зберігання даних

Для генерації змінних-членів класу нам потрібно знати про них дві речі – тип обумовленою змінної і її імя У даній версії генератора ми зробимо цілком логічне припущення про те, що типи змінних збігаються з типами властивостей, які вони представляють У більшості випадків наше припущення буде цілком виправдано Наш генератор не підтримує індексовані властивості (на жаль, це ще одне обмеження), так що нам не доведеться розбирати ті випадки, при яких можуть виникнути відмінності

Визначивши тип змінної, можна переходити до імені На наше щастя в CBuilder існують певні угоди, які допоможуть нам визначитися з іменами Імена змінних-членів класу, що визначають властивості, формуються шляхом додавання великої літери F до імені властивості, так ми і будемо поступати для наших компонентів Зрештою, угоди адже існують для того, щоб їм слідувати, що не так

Наступна проблема, з якою нам доведеться зіткнутися, повязана з тим, що нам треба генерувати змінні класу не для всіх властивостей, а тільки для тих, що не були підсаджені в клас Насправді, якби ми навіть і згенерували змінні для підсаджених властивостей, вони б стали локальними всередині нашого класу і нам не було б від них ніякого пуття Як нам впоратися з цією проблемою Відповідь лежить в тому, як ми створювали нові властивості Якщо ви памятаєте, для них ми записували і тип, і імя властивості, а у підсаджених властивостей – тільки імя Отже, ми можемо перевіряти властивості на наявність прогалини в імені (пробілу між типом властивості і його імям), який буде служити нам своєрідним індикатором Так ми і поступимо Ось код для функції GenerateMemberVariables, В якій ми будемо здійснювати цю перевірку:

void TPagesDlg::GenerateMemberVariables(FILE  *fp)

{

/ / Шукаємо властивості, в описі яких є прогалина

/ / Цим властивостям треба зіставити змінні класу

for ( int i=0 i&ltListBox4-&gtItems-&gtCount ++i )

{

AnsiString s = ListBox4-&gtItems-&gtStrings[i] if ( strchr( sc_str(), ) )

{

char szProp[ 256 ] char szType[ 256 ]

/ / Можемо знайти кінець рядка

s += &quot "

int nPos = GetWordQuoted( sc_str(), szType, NULL) GetWordQuoted( sc_str()+nPos, szProp, NULL)

/ / Створюємо змінну класу цього типу

fprintf(fp, &quot %s F%s\n&quot, szType,

szProp )

}

}

}

Для самих властивостей ми викликаємо методGenerateProperties Цей метод теж виконує різні дії залежно від того, чи є властивість підсадженим Якщо так, то генерується прототип властивості:

__property Fred

З іншого боку, якщо властивість є новим, нам потрібно його повний опис:

__property int Fred={read=FFred, write=FFred}

Ось як виглядатиме повний код для методу GenerateProperties: void TPagesDlg::GenerateProperties(FILE *fp)

{

/ / Шукаємо властивості, в описі яких є прогалина

for ( int i=0 i&ltListBox4-&gtItems-&gtCount ++i )

{

AnsiString s = ListBox4-&gtItems-&gtStrings[i] if ( strchr( sc_str(), ) )

{

char szString[ 256 ] char szProp[ 256 ] char szType[ 256 ]

strcpy ( szString, sc_str() )

// So we can find the end of the string strcat ( szString, &quot &quot )

int nPos = GetWordQuoted( szString, szType, NULL) GetWordQuoted( szString+nPos, szProp, NULL) fprintf(fp, &quot __property %s %s={read=F%s, write=F%s}\n&quot,

szType, szProp, szProp, szProp )

}

else

fprintf(fp, &quot __property %s\n&quot, sc_str())

}

}

У заголовному файлі методи вже визначені за допомогою нашого методу GenerateHeader Всі методи зберігаються в списку в тому форматі, в якому представляються в заголовному файлі, незалежно від того, були вони скопійовані туди або ж створені заново, тому ми можемо просто копіювати елементи списку в заголовний файл

Згенерувавши заголовний файл, можна приступати до створення вихідного файлу Метод GenerateSourceFile використовується для створення файлу і додавання в нього всіх синтаксичних конструкцій, необхідних для створення вихідного коду для методів Ось як виглядає код методу GenerateSourceFile:

void TPagesDlg::GenerateSourceFile(FILE *fp)

{

fprintf(fp,&quot//——————————————————-   ——————–\n&quot)

fprintf(fp,&quot#include  &ltvcl\\vclh&gt\n&quot) fprintf(fp,&quot#pragma  hdrstop\n\n&quot)

fprintf(fp,&quot#include \&quot%s\&quot\n&quot,  HeaderFile-&gtTextc_str())

fprintf(fp,&quot//——————————————————-   ——————–\n&quot)

fprintf(fp,&quotstatic inline %s *ValidCtrCheck()\n&quot, ClassName-&gtTextc_str())

fprintf(fp,&quot{\n&quot)

fprintf(fp,&quot return new %s(NULL)\n&quot, ClassName-&gtTextc_str()) fprintf(fp,&quot}\n&quot)

fprintf(fp,  &quot//——————————————————  ———————\n&quot)

fprintf(fp, &quot__fastcall %s::%s(TComponent* Owner)\n&quot, ClassName-&gtTextc_str(),

fprintf(fp,&quot : %s(Owner)\n&quot, ComboBox1-&gtTextc_str()) fprintf(fp,&quot{\n&quot)

fprintf(fp,&quot}\n&quot) GenerateMethodsSource(fp)

}

Цей метод надзвичайно простий Він створює на початку вихідного файлу список підключаються файлів, включаючи і заголовний файл для нашого класу Для створення коректного примірника компонента, який можна було б використовувати в редакторі форм, генерується викликається VCL метод ValidCtrlCheck Після цього створюється основний конструктор, який потрібно VCL для виклику конструктора базового класу Як неважко здогадатися, весь цей код є узагальненим варіантом коду базового компоненту

Останній етап – це генерація окремих методів класу Це здійснено в методі

GenerateMethodSource Створіть цей метод і додайте в нього наступні рядки:

void TPagesDlg::GenerateMethodsSource(FILE *fp)

{

for ( int i=0 i&ltListBox2-&gtItems-&gtCount ++i )

{

AnsiString s = ListBox2-&gtItems-&gtStrings[i] char szString[ 256 ]

char szType[ 256 ]

strcpy ( szString, sc_str() )

int nPos = GetWordQuoted( szString, szType, NULL)

/ / Позбутися від хвоста рядка szString [strlen (szString) -1] = 0

fprintf(fp, &quot%s %s::%s\n&quot, szType, ClassName-&gt

Textc_str(), szString+nPos ) fprintf(fp, &quot{\n}\n&quot)

}

}

На даний момент у нас вийшло цілком працездатний додаток для генерації компонентів Проте мета приклад була створити Майстра Давайте тепер займемося безпосередньо цим

Джерело: Теллес М – Borland C + + Builder Бібліотека програміста – 1998

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*