Quantcast
Channel: Latest blog entries - Embarcadero Community
Viewing all 1683 articles
Browse latest View live

Delphi Academy: Prototipos de UI

$
0
0

Esta semana llevamos al aire otro episodio sobre desarrollo de apps en el Delphi Academy. En este episodio presentamos algunos ejemplos que pueden ser utilizados como base para la creación de nuevas aplicaciones, los llamados prototipos.

El objetivo fue concentrar en estos ejemplos el conocimiento obtenido en los episodios anteriores sobre desarrollo móvil, en busca de modelos profesionales de apps.

También hablamos sobre diálogos modales, y aplicaciones con múltiples formularios, entonces si tiene dudas en esta área, vale la pena una visita a nuestro playlist aquí!

Aquí hay algunos ejemplos de los proyectos que se han puesto a disposición en nuestro repositorio de fuentes:

Realmente espero que sea útil de alguna manera, y en el próximo episodio, hablaremos de entrada de datos en FMX!

Inscripciones siempre abiertas, 24×7!



Read More

Neue Videoreihe / Video Tutorial #1: AQTime

$
0
0

Im Zuge meiner Arbeit wird es alle paar Tage (10<n<20) ein Kurzvideo geben; rund um das RAD Studio / Delphi / C++Builder.

Dauer ist maximal 5 Minuten und stellt (ohne Folien) ein Thema kurz dar. Es hat keinen Anspruch auf einen allumfassenden Einblick, sondern nur auf eine kurze Einführung.

Hier das erste Video:


[YoutubeButton url='https://www.youtube.com/watch?v=OZzY_2JqdGY']

Read More

改变TLabel字型和颜色

$
0
0

最近收到几位使用者的来信都是和如何改变FireMonkey TLabel组件的字型和颜色, 这几位使用者都是直接改变TextSettings特性中的Font子特性但却无法改变字型和颜色, 因此来信询问.

 

FireMonkeyTLabel组件控制组件的字型和颜色其实有2个特性, 分别是StyledSettingsTextSettings, 而且StyledSettings特性优先于TextSettings特性,在内定上StyledSettings特性会设定它的SizeFontColor子特性, 因此StyledSettings特性会根据父代组件的Style来决定TLabel组件控制组件的字型和颜色而忽视TextSettings特性的设定值.因此开发人员可以藉由取消StyledSettings特性的SizeFontColor子特性就可以再设定TextSettings特性的Font子特性来改变字型和颜色:

如果开发人员是便用程序代码动态建立TLabel组件, 那么可以使用下面的程序代码把StyledSettings特性的SizeFontColor子特性取消即可:

 

  myLabel= new TLabel(this);

  myLabel ->Parent = this;

  myLabel ->StyledSettings = lab2->StyledSettings >> TStyledSetting::FontColor;

  myLabel ->StyledSettings = lab2->StyledSettings >> TStyledSetting::Size;

 

  myLabel ->TextSettings->FontColor = TAlphaColorRec::Crimson;

….


Read More

如何改變FireMonkey TLabel字型和顏色

$
0
0

最近收到幾位使用者的來信都是和如何改變FireMonkey TLabel元件的字型和, 這幾位使用者都是直接改變TextSettings特性中的Font子特性但卻無法改變字型和, 因此來信詢問.

 

FireMonkeyTLabel元件控制元件的字型和色其實有2個特性, 分別是StyledSettingsTextSettings, 而且StyledSettings特性優先於TextSettings特性,在內定上StyledSettings特性會設定它的SizeFontColor子特性, 因此StyledSettings特性會根據父代元件的Style來決定TLabel元件控制元件的字型和色而忽視TextSettings特性的設定值.因此開發人員可以藉由取消StyledSettings特性的SizeFontColor子特性就可以再設定TextSettings特性的Font子特性來改變字型和:

如果開發人員是便用程式碼動態建立TLabel元件, 那麼可以使用下面的程式碼把StyledSettings特性的SizeFontColor子特性取消即可:

 

  myLabel= new TLabel(this);

  myLabel ->Parent = this;

  myLabel ->StyledSettings = lab2->StyledSettings >> TStyledSetting::FontColor;

  myLabel ->StyledSettings = lab2->StyledSettings >> TStyledSetting::Size;

 

  myLabel ->TextSettings->FontColor = TAlphaColorRec::Crimson;

….


Read More

Delphi and C++Builder Fest in Eindhoven Today

$
0
0

Just back home from fabulous Delphi and C++Builder "The LAB" conference today in Eindhoven (the city of Philips), The Netherlands.

What a day! Lots of programmers gathered to learn something new and get inspired! One photo is a thousand of words:-)

The LAB conference started from few words from Raymond Horsten, who is owner and the main director of Barnsten, the company that represents Embarcadero to Benelux and France, and organized this great international event!

After the keynote Jens Fudge, Embarcadero MVP from Denmark, did an excellent FireDAC "tips and tricks" session including showing the new Google Calendar FireDAC Enterprise Connector.

The problem with this big conferences is choices. After Jens session, there were three simultaneous tracks to choose from. So many big experts, but you cannot be at all sessions. My first choice was the session from David Millington, Embarcadero C++Builder Product Manager. I'm not a C++ person, but it was very interesting to learn why CLang compilers and C++11 language improvements are the way to go. New initialisers syntax, functors, "auto" types, smart pointers... 

Choices again. Danny Wind from the The Delphi Company is one the best Delphi experts I know. His Delphi VCL Windows 10 modernisation was really well attended and and very informative.

The most inspiring session for me today was by Stephen Glienke. It was about "reactive" library for Delphi. It was all about moving Object Pascal language into the future. His library is impressive. Fluent syntax, generics, anonymous code. Very impressive!

There were so many other interesting sessions, but I just could not be in too many places at the same time!

The closing session was fun. David Millington showed how you can easily consume C++ libraries from Delphi code in a very elegant way. You just declare a Delphi class with virtual, abstract methods, generate C++ headers and implement it in pure C++. That's a way to go to consume all cool C++ libraries from Delphi and a reason to upgrade to RAD Studio:-)

See You next year!


Read More

Modernize your apps with new styles from DelphiStyles.com

$
0
0

KSDev, the company behind DelphiStyles.com, has released some stunning new styles for VCL and FireMonkey.  This includes a number of platform specific themes for Windows and macOS. 


Read More

Generate Cross Platform Dynamic Forms At Runtime From JSON In Delphi 10.2.1 Tokyo

$
0
0

The Hospitality Survey Client project is part of the Hospitality Survey App template for Delphi 10.2.1 Tokyo that Embarcadero has released through their GetIt platform. The Hospitality Survey App consists of four different projects. In this blog post I will cover the dynamic form generator that is built into the Hospitality Survey Client project. Also keep in mind that the client can be deployed to Android, iOS, macOS, and Windows with a single code base and a single responsive UI.

Basically how it works is on the server there is a database table which contains the list of survey questions to be asked to patrons from each tenant (in this case restaurant). The /survey/ end point in RAD Server is called from TBackendEndpoint (which is basically a TRESTClient) in the Survey App Client. The RAD Server end point returns the contents of the Questions table as the FireDAC JSON format. You can customize the questions using the Hospitality Survey Editor. The Client saves out the FireDAC JSON to a surveys.json file which is then loaded into an TFDMemTable. The GenerateSurvey() function (see below) loops through the records in the TFDMemTable and creates a TFrame for each record. Each record corresponds to a question in the database and you can see the different fields in a record below:

  • ID - An ID for the question.
  • name - A short name for the question with no spaces.
  • title - The text of the question as it will appear in the survey.
  • type - The type of question controls which question template is loaded on the client. The existing types are: rating, yesno, edit, options
  • options - If the type of the question is set to options this field is used to populate the options. It's value is a JSON array of options.
  • value - The value is where the user submitted data is stored. It can be left blank but could be used to provide a default answer.
  • category - The category of the question. This field is provided for expandability.
  • tenant_id - The tenant ID of the question. If the tenant_id field is blank all tenants will get the question. If it is set to a tenant only that tenant will get the question.

The Type column determines which TFrame is loaded for that record. The built in types are: rating, yesno, edit, options. You can add your own row types as well by modifying the GenerateSurvey() procedure. You can see the units below for each of the dynamic TFrames including a header frame and a complete button frame for submitting the form.

  • uSurveyHeaderFrame.pas - Contains the header for the top of the survey.
  • uRatingBarFrame.pas - Contains the star rating track bar survey question type.
  • uYesNoFrame.pas - Contains the Yes/No survey question type.
  • uEditFrame.pas - Contains the edit survey question type.
  • uComboBoxFrame.pas - Contains the combo box survey question type.
  • uCompleteFrame.pas - Contains the complete button for the survey.

The GenerateSurvey() procedure itself is pretty simple. It loops through the TFDMemTable dataset and checks the type field to see which TFrame to load and populate for that record. The options field is a JSON array that is used to populate the values for the yesno type and options type. The ID field is used to mark the TFrame with the specific question it was created from (FrameItem.Tag := BindSourceDBForm.DataSet.FieldByName('ID').AsInteger;) so that the value field can be filled out with the answer from the user.

//
//
procedure TMainForm.GenerateSurvey(Sender: TObject);
var
  FrameItem: TFrame;
  FieldType: String;
  FieldCategory: Integer;
  JSONArray: TJSONArray;
  I: Integer;
begin
  FrameItem := TSurveyHeaderFrame.Create(TFMXObject(Sender));
  FrameItem.Parent := TFMXObject(Sender);
  FrameItem.Name := 'FSurveyHeader';
  FrameItem.Align := TAlignLayout.Top;
  FrameItem.Position.Y := 0;

  BindSourceDBForm.DataSet.First;
  while not BindSourceDBForm.DataSet.Eof do
  begin
    FieldCategory := BindSourceDBForm.DataSet.FieldByName('category').AsInteger;

      FieldType := BindSourceDBForm.DataSet.FieldByName('type').AsString;
      if FieldType = 'edit' then
      begin
        FrameItem := TEditFrame.Create(TFMXObject(Sender));
        FrameItem.Parent := TFMXObject(Sender);
        TEditFrame(FrameItem).QuestionText.Text :=
          BindSourceDBForm.DataSet.FieldByName('title').AsString;
      end;
      if FieldType = 'yesno' then
      begin
        FrameItem := TYesNoFrame.Create(TFMXObject(Sender));
        FrameItem.Parent := TFMXObject(Sender);
        TYesNoFrame(FrameItem).QuestionText.Text := BindSourceDBForm.DataSet.FieldByName('title').AsString;
        JSONArray := TJSONObject.ParseJSONValue(BindSourceDBForm.DataSet.FieldByName('options').AsString) as TJSONArray;
        for I := 0 to JSONArray.Count - 1 do
        begin
          case I of
            0:
              begin
                TYesNoFrame(FrameItem).ValueSpeedButton1.Text := JSONArray.Items[I].Value;
                TYesNoFrame(FrameItem).ValueSpeedButton1.GroupName := BindSourceDBForm.DataSet.FieldByName('name').AsString;
              end;
            1:
              begin
                TYesNoFrame(FrameItem).ValueSpeedButton2.Text := JSONArray.Items[I].Value;
                TYesNoFrame(FrameItem).ValueSpeedButton2.GroupName := BindSourceDBForm.DataSet.FieldByName('name').AsString;
              end;
          end;
        end;
        JSONArray.Free;
      end;
      if FieldType = 'rating' then
      begin
        FrameItem := TRatingBarFrame.Create(TFMXObject(Sender));
        FrameItem.Parent := TFMXObject(Sender);
        TRatingBarFrame(FrameItem).QuestionText.Text := BindSourceDBForm.DataSet.FieldByName('title').AsString;
      end;
      if FieldType = 'options' then
      begin
        FrameItem := TOptionsFrame.Create(TFMXObject(Sender));
        FrameItem.Parent := TFMXObject(Sender);
        TOptionsFrame(FrameItem).QuestionText.Text := BindSourceDBForm.DataSet.FieldByName('title').AsString;
        JSONArray := TJSONObject.ParseJSONValue(BindSourceDBForm.DataSet.FieldByName('options').AsString) as TJSONArray;
        TOptionsFrame(FrameItem).ValueComboBox.Items.BeginUpdate;
        for I := 0 to JSONArray.Count - 1 do
        begin
          TOptionsFrame(FrameItem).ValueComboBox.Items.Add(JSONArray.Items[I].Value);
        end;
        TOptionsFrame(FrameItem).ValueComboBox.Items.EndUpdate;
        JSONArray.Free;
      end;
      FrameItem.Name := 'F' + BindSourceDBForm.DataSet.FieldByName('ID').AsString;
      FrameItem.Align := TAlignLayout.Top;
      FrameItem.Tag := BindSourceDBForm.DataSet.FieldByName('ID').AsInteger;
      FrameItem.Position.Y := BindSourceDBForm.DataSet.FieldByName('ID').AsInteger * 100;

    BindSourceDBForm.DataSet.Next;
    Application.ProcessMessages;
  end;

  FrameItem := TCompleteFrame.Create(TFMXObject(Sender));
  FrameItem.Parent := TFMXObject(Sender);
  FrameItem.Name := 'FComplete';
  FrameItem.Align := TAlignLayout.Top;
  FrameItem.Position.Y := 1000000;
  TCompleteFrame(FrameItem).CompleteButton.OnClick := CompleteClick;

end;

The selected option in each TFrame gets sent back to the TFDMemTable via the UpdateValueByID() procedure as you can see below. In the below code the Self.Tag field corresponds to the ID field of the question.

//
//
procedure TOptionsFrame.ValueComboBoxChange(Sender: TObject);
begin
  MainForm.UpdateValueByID(Self.Tag,ValueComboBox.Items[ValueComboBox.ItemIndex]);
end;

The UpdateValueByID() procedure uses the Locate() procedure on the DataSet to find the correct record and then update the value field.

//
//
procedure TMainForm.UpdateValueByID(ID: Integer; const Value: string);
begin
  if BindSourceDBForm.DataSet.Locate('ID', VarArrayOf([ID]), []) = True then
  begin
    BindSourceDBForm.DataSet.Edit;
    BindSourceDBForm.DataSet.FieldByName('value').AsString := Value;
    BindSourceDBForm.DataSet.Post;
  end;
end;

Once the survey has been completed by the user then the entire contents of the TFDMemTable are saved out to the FireDAC JSON format and uploaded back to the server via a POST from a TBackendEndpoint component to the /survey/complete endpoint. In the case of the Hospitality Survey App all of the uploaded records are saved for each collected survey. This allows the survey questions to be created, removed, and changed without affecting any of the existing surveys that have already been collected.

Want to know more? Check out the full Deep Dive into the Hospitality Survey App template for Delphi 10.2.1 Tokyo.


Read More

Experimenting with Neural Networks – Part 1


Из Воронежа с любовью...

$
0
0

...к Delphi и C++Builder.

На этой неделе состоялся долгожданный для нас "живой" семинар Embarcadero в городе Воронеж, организованный совместно с нашим постоянным воронежским партнером - компанией SoftLine. 

В Воронеже живут очень занятые люди. Это было заметно и в утренние часы, когда жители направлялись на работу, это было заметно во время общения с участниками. Вероятно поэтому, на столе осталось некоторое количество невостребованных табличек с именами зарегистрировавшихся, но не сумевших появиться на семинаре людей. Тем не менее, присутствовавших участников было вполне достаточно, чтобы ощутить неподдельный интерес к темам, заявленным в программе семинара, и с энтузиазмом рассказывать о и показывать широкие возможности последней версии RAD Studio 10.2. 

Несколько лет назад я выступал на аналогичном семинаре в Воронеже с рассказом об инструментах Embarcadero для работы с базами данных, поэтому в заполненном зале присутствовали некоторые знакомые лица. Но много пришло коллег, принимавших участие в нашем семинаре  в первый раз. Значительная часть в зале представляла образованную, красивую и активную молодежь. Надеюсь, содержание наших докладов не разочаровало их. 

Разумеется, в рамках запланированных и отведенных нам часов практически невозможно познакомить со всеми возможностями наших решений. В плане семинара мы предусмотрели как доклады более обзорного плана, так и коснулись некоторых интересных тем более подробно. Не подвела и техника, позволив показать все наиболее интересные примеры использования RAD Studio без сбоев.

Очевидно, вызвавшей самый большой интерес темой стала разработка на Delphi для Linux. Приятно удивил интерес многих слушателей к вопросам разработки приложений, работающих  с устройствами IoT, созданию REST-систем и работы с данными.

Во время доклада Дениса Зубова - ведущего разработчика для Delphi нашего технологического партнера, компании Fast Report - я был среди слушателей в зале и мне прекрасно были заметны тот интерес и те эмоции, которые испытывали участники при виде потрясающих примеров интерактивных отчетов, которые показывал Денис.

Пользуясь случаем, хотел бы отметить четкую организацию семинара, доброжелательность и отзывчивость ко всем нашим просьбам и маленьким поручениям сотрудников компании Softline в Воронеже. Спасибо! 

В Воронеже живут занятые люди. Вероятно поэтому, многие разработки ведутся не на последних версиях Delphi или C++Builder. Надеюсь, что нам удалось дать представления, какими замечательными новыми возможностями можно воспользоваться, чтобы дать вторую, еще более успешную жизнь имеющимся приложениям и системам, для разработки новых, передовых систем последнего технологического поколения, решения большего числа корпоративных задач и реализации оригинальных бизнес-идей.

До новых встреч!  

 

   


Read More

Delphi / C++Builder Starter チュートリアルシリーズ スペシャル 3限目 「作成したアプリケーションが動かない!?その解決法を学ぶ」[JAPAN]

$
0
0

Delphi / C++Builder Starter Edition が無償でご利用いただけるようになって、1年が経ちました。そこで無償提供開始1周年を記念して、2017年9月22日(金)に「Delphi / C++Builder Starter チュートリアルシリーズ スペシャル」というWebセミナーを開催いたしました。

今回のWebセミナーは放送時間1時間を3つのセッションに区切って放送いたしましたが、ライブセミナーということでオンタイムにご視聴頂けなかった方もいらっしゃると思います。

そこでこの記事では、3限目 17:40 ~ 18:00 の20分間でお送りした「作成したアプリケーションが動かない!?その解決法を学ぶ」の一部を抜粋してご紹介いたします。


Read More

Starter ユーザーのために ProfessionalやEnterprise版で何ができるようになるのかまとめる[JAPAN]

$
0
0

Starter版の無期限無償公開から約1年が経過しました。 Delphi, C++Builder ともに数多くの方々にダウンロードいただき、いまだに好評であります。一年前に作成した無償公開を紹介するブログと、その後の更新版の入手方法を記載したブログはいまだに人気があり、アクセス数を増やし続けています。

Starter Editionは無償提供されている、Windows 32bit向けに絞った開発環境です。Object Pascal(Delphi)やC++といったプログラム言語を学ぶ環境としても利用価値がありますし、Windows向けの単体アプリを開発するならば、十分に役立ちます。

しかしながら、上位有償のProfessional版やEnterprise版であればなおのこと、やりたいことがより効率的にできる機能が搭載されています。そこでStarterユーザーのために、もし上位版を手に入れたならどんなことができるようになるのか、どんなメリットをもたらすのか、メジャーなところを紹介してゆきたいと思います。

上位版エディションについて

「上位版」を使ってどんなことができるのか、を説明する前に、「上位版」とは一体何なのか、どんな種類が存在するのか明らかにしておきます。製品の種類と、エディション、というカテゴリ分けができます。
まず、【製品の種類】は三つに分けられます

製品名 内容
RAD Studio Delphi・C++Builder両方入りスイート
Delphi Object Pascal(Delphi)言語が利用できる開発環境単体
C++Builder C++言語が利用できる開発環境単体

いずれの製品も開発デバッグ等が可能な統合開発環境で、VCLやFireMonkeyといったビジュアルコンポーネントを使って効率的に開発できる特徴を持ち合わせているところは変わりませんが、プログラミングに使える言語がObject Pascalのみか、C++のみか、またはその両方が使えるのか?といったところで違いがあります。またDelphi版とRAD Studioには、コンポーネントのソースコードが付属しており、コンポーネントの動作の詳細に至るまでソースコードで確認することができます。

そして各製品には4つの【エディション】が用意されます。(RAD StudioはStarterエディションが無いので3つのエディションとなる)

エディション 概略
Starter DelphiとC++Builderのみに用意。Win32向け対応。 無償提供版、商用利用には制限在り
Professional Win32/64,iOS,Android,macOS向け開発(※1)、ローカル/組み込みデータベース接続
Enterprise Pro版機能に加え、Linux向け開発(サーバーサイド)、C/Sデータベースアプリケーション構築(※2)、サーバーサイド開発ライセンス
Architect Enterprise版機能に加え、データモデリングツールが付属
  • (※1)Delphi / C++Builder 単体製品の場合、Professinal 版での iOS, Android 向け開発はオプション(追加購入)となります。
  • (※2)Professional版にもオプションで追加することができます。

機能的には Starter < Professional < Enterprise < Architect と右に行くほど高機能になります。
詳しい機能表は PDFで提供されている機能一覧で確認できます。また概略はエンバカデロのWebページの製品ページにある「製品エディション」情報で確認できます。

上位版を手に入れたならどんなことができるようになるのか

さて、製品名とエディションを理解していただいたところでいよいよ本題。どんなやりたいことを実現できるのか、という話にはいってまいります。

iOS/Android上で動作するモバイルアプリの開発が可能に

iOS, macOS, android向けのアプリ開発が可能になります。 (iOSとmacOSアプリの開発には macOSの搭載されたMac機が別途必要)
しかも、Windows開発と同じ手法のビジュアル開発で、かつ、UIとプログラムを共通で使えるクロスプラットフォーム開発が可能です。 簡単に言えば、一つのUIと一つのプログラムを作ったら、あとは各OS向けにビルドするだけで、各OS対応のアプリができる、というものです。Starterで覚えたWindows向け開発のスキルを生かして、そのままiOS/Android/macOSのアプリ開発に入れるのが嬉しいですね。

データベースにコンポーネントを使って接続できるので、データベースを必要とするアプリ開発が効率的

データベースに接続するためのコンポーネント「FireDAC」が用意されていますので、データベース接続を必要とするアプリを効率的にビジュアル開発で作ることができます。かんたんなデータベースのテーブル取得や表示、レコードの登録や更新削除などの基本的な部分は、コンポーネント用意されているので、こーどを書かずに作ることができるくらい簡単です。より細やかなデータベース操作にSQL文やストアドプロシージャを活用するこことが必要になりますが、それらSQL文やストアドプロシージャもコンポーネントを使って管理することができます。

サーバーサイドも書けます

サーバーサイドのロジックも開発することができます。REST/JSON形式でクライアントとやり取りするAPIを用意できます。データベースへのアクセスはFireDACを使えるので開発も効率よくに行えます。またユーザー管理や、モバイルデバイスへの通知機能も備えているので、開発工数に繋がります。

ユニットテスト機能も含んでいるので、継続的な開発検証に役立ちます

テスト フレームワーク DUnit が統合されていて、ユーザーのアプリケーションに対して自動化されたテストケースを実行できます。アプリケーション内のクラスとメソッドのテストの作成と、テスト実施ができ、継続した開発において、アプリケーションの安定性の向上が期待できます。変更を加えるたびにコード全体で標準のテスト一式を実行すれば、開発サイクルの初期段階で、問題を検出、つぶしておくこともできるようになるでしょう。

コード保管。クラス記述補完機能も

Starterにもコード保管が付いていますが、よりすごいのがクラス記述補完。宣言を描いておくとクラスの実装部のテンプレートを記述してくれたり、プロパティ実装部のテンプレートを記述してくれたり、と、コーディングの手助けになります。

Delphiならソースも付いています

Delphi(とRADStudio)ならFireDACやFireMonkey、VCLといったコンポーネントのソースコードが付いてきます。これらのコンポーネント・フレームワークはDelphiで記述されていますので、実際、コンポーネントがどのようなロジックで動作しているのか、詳細に確認できます。提供されているコンポーネントがブラックボックスで、バグのありかがどこだかわからない!なんてことにはなりません。

広がります

上位版における機能面での有意性、できることをさらっとご紹介しました。これら機能を使って作りたいものを自由に開発できます!モバイルアプリや、業務向けの事務処理、データ処理や、3Dの研究、宇宙開発にも使われています。この1年でStarterをつかって培ったスキル、または昔取った杵柄、初期のDelphiで鍛えた腕前をぜひ活用してください!

Starterから上位エディションへアップグレードするチャンス

今回、無料で利用できるStarter Editionから、上位エディションへの特別アップグレードパスを期間限定で用意しています。この機会に、ビジュアル開発の世界をマルチデバイス開発、データベース、クラウド、IoTなどとつながるアプリケーションの構築まで、その世界を広げてみませんか?

期間限定2017年9月27日まで!くわしくはこちらから:RAD Studioキャンペーンページ


Read More

An Overview of Elements of Functional Programming in C++ (continuation)

$
0
0

Part II

Where it is told about "functional containers" - data structures that can store pointers to functional objects of various types

std::function

While the functional paradigm proclaims that "everything is a function," in imperative programming we sometimes encounter the need to manipulate functions like with ordinary objects. Usually this happens by creating a pointer to the address of the procedure or method. However, in this case and others, high-level languages leave it up to us the use of the function pointer, the care of the accordance to the call signature, the passed parameters and other low-level pleasures. Naturally, if we intend to manipulate variables that store pointers to procedures or methods, functors and lambda functions, we inevitably run into headaches due to the different intrinsic nature of these objects. Meanwhile, we would like to think about our algorithm, and not to unravel the puzzles that the compiler tosses us. All of the above objects have only one, but extremely important, common  property - they are callable. Is there a way to refer to them in a uniform style?

Yes, and this way is the using of  std::function.

Instances of std::function can store, copy, and invoke any callable target functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members

cppreference says. This is what you need for our “functional” purposes - we can think of this (and use it) as a variable in the our “functional” world.

C++ is to this day is a strongly typed language. Together with std::function we can refuse to use arbitrary pointers for tricks with references to our functions, functors, member functions, and lambdas. In the world of function pointers, this is something like the legalization of types for such entities. We can define a template, declare a variable of a callable type and assign necessary values to them when we want.

Syntax

Class template std::function is a general-purpose polymorphic function wrapper.

template< class > class function;

or

template< class R, class... Args > class function<R(Args...)>

 

Example

Let's look at the example of a console application which generates a vector of integers and calculates a number of occurrences in a specified diapason.

We will use  std::for_each  and std::count_if  algorithms from STL to сircumvent all elements (in particular, for print) and to count special condition.

The functor class Adder will be used to fill a vector with integer elements. A definition of its operator()method differs from functor Generator considered in one of the previous examples in Part I. Here we add vector elements ourselves in a for loop, and don’t use std::generate. In the operator of the functor an argument from the outside is given to calculate the new element. Thus, we do not need to store it inside.

class Adder {

...

public: void operator()(int val) { _vec->push_back(val); }

...

};

 

Pay attention to how we pass the vector to the inside of the constructor of the Adder functor, and store the pointer to it in a private field.

private: vector<int>* _vec;

...

Adder(vector<int>& vec) { _vec = &vec; };



We can not declare a vector variable directly in a private field because even if we pass the original vector by reference into the constructor, this field will be assigned a local copy, to which elements will be added.

...

Adder add(v);

...

 

Note that actions stored in the  std::function variable can be of a completely different nature,  matching by the required call signature only. We can combine heterogeneous actions in this role, and it is possible to think about applications when one can consider not the consistent act of the same operation to set of objects, but the impact of an ordered (or unordered!) set of operations to the same object.

Let's look at the example of a conveyor belt in terms of trash sorting, the subject is an employee or an automated machine performing such sorting. If we consider the assembly line of airplanes, it may be more convenient to consider the sequence of operations for each airplane separately. In fact, we are dealing with the product of sets of objects and operations, and this statement of the problem determines the angle of view on the situation. The std::function gives us the ability to naturally manipulate operations - it already smells like true functional programming!

Returning to our example we declare a variable for the abstract operation:

function<void(int)> operation;

 

We can assign to this variable functor Adder, which knows how to add a passed value as a new element into the vector and later re-assign it to the  lambda expression that print all passed values.

Checking the condition requires passing a predicate. Now we are not limited in the choice of tools regarding what this predicate must be, and std::function gives us additional flexibility here.

Firstly, define a functor named Predicator that will store a border of ranges in its private fields and return result to check its operator().

Alternatively,  we can declare the predicate as a lambda expression where checking the value passed an  argument, while diapason borders are passed via capture scope. Looks rather natural, doesn’t it? We don’t want to remember border values every time while performing a check for some number. At the same time, we don’t use global variables in an uncontrolled manner.

In this code, you will see how a variable of type

function<bool(int)> predicate


and the assignment of lambda performed. Later we use the ability to assign this variable a functional object. However, we can define a lambda right at the location of calling count_if, if this makes the code visible and the following is done too.

 

#pragma hdrstop

#pragma argsused

#ifdef _WIN32
#include <tchar.h>
#else
  typedef char _TCHAR;
  #define _tmain main
#endif

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

using namespace std;

class Adder {
	private: vector<int>* _vec;
	public: void operator()(int val)
	{
		_vec->push_back(val);
		cout << val << " inserted" << endl;
	}
	public: Adder(vector<int>& vec) { _vec = &vec; };
};

class Predicator {
	private:
		int _a;
		int _b;
	public:
	bool operator()(int n)
	{
		return (_a <= n) && (n < _b);
	}
	Predicator(int a, int b)
	{
		_a = a;
		_b = b;
	}
};



typedef void (*prfn)(int value);
prfn printfn = [](int value){ cout << value << " "; };



 int _tmain(int argc, _TCHAR* argv[])
 {
	vector<int> v;
	int m;
	cout << "Enter array size: ";
	cin >> m;


	Adder add(v);
	function<void(int)> prn = printfn;

	function<void(int)> operation;



	operation = add;
	for (int val = 0; val < m; val++)
	{
		operation(val); /*add(val);*/
	}

	operation = prn;
	cout << "Content of array: ";
	for_each(v.begin(), v.end(), operation);  /*prn*/
	cout << endl;;

	int lowerBound = 0, upperBound = 0;
	cout << "Enter the bounds of the range: ";
	cin >> lowerBound >> upperBound;

	cout << "Number of elements in the interval [" << lowerBound << ", " << upperBound << ")" << endl;
	int result;

	//using of lambda-expression
	result = count_if(v.begin(), v.end(),
		[lowerBound, upperBound] (int _n)
		{
			return lowerBound <= _n && _n < upperBound;
		}
	);
	cout << "Calculated with lambda-expression: " << result << endl;

	//using of functor
	Predicator prdobj = Predicator(lowerBound, upperBound);
	result = count_if(v.begin(), v.end(), prdobj);
	cout << "Calculated with functor: " << result << endl;

	//std::function predicate based on the lambda-expression
	function<bool(int)> predicate =  [lowerBound, upperBound] (int _n)
		{
			return lowerBound <= _n && _n < upperBound;
		}
	;
	result = count_if(v.begin(), v.end(), predicate);
	cout << "Calculated with std::function predicate based on the lambda-expression: " << result << endl;

	//std::function predicate based on the functional object
	predicate =  prdobj;
	result = count_if(v.begin(), v.end(), predicate);
	cout << "Calculated with std::function predicate based on the functional object: " << result << endl;

	cin.ignore(2);
	return EXIT_SUCCESS;

 }

 

 

 

 It is possible that these are far-fetched examples and you are not very impressed. That said, there is a case where it's not easy to do without using std::function.

What makes functional programming functional is that a function can return a function, and does so in many cases. Likewise, a lambda can generate a lambda. In addition, the role of closures, of course, is not to make code more concise although for imperative programming this is a serious argument.

Each lambda function has some context at the time of its call. In the body of a lambda, we have access to some variables of the calling code, accordingly to its capture-list. With the help of these variables we can define a new lambda function and return it as a result. After that, the calling code section can exit the scope, but the generated lambda function continues to live its life and can be called at any time!

At the same time, the variables that are “closed” in this way must be available all this time. Even after we forget about the origin of our lambda function! There are interesting effects, which can be very specific for each language that supports closures.

Let's try to solve the well-known problem of computing the number of elements of a vector that belong to a given interval using lambdas, but we will try to do it without global variables.

After the familiar code responsible for initializing the vector, we are faced with the definition of the lambda function:

auto lambdagen = [](bool including)->function<bool(int)>

 

This is exactly the definition that we could not make previously when we considered the possibility of generating a lambda function as a result! Note that the auto keyword here doesn’t define the type of the result of the lambda function, but the type of the variable that will store the reference to such a function. At the same time the lambda function will return a variable of a new type for us - function<bool(int)> and this type is the required predicate type!

Next, in the body of the lambdagen function we declare lowerBound  and upperBound variables which represent the borders of the interval. Note, that they are local variables inside the body of the lambda-function only!

It’s assumed here that including the parameter of the lambdagen  allows us to interpret the interval type as closed  [lowerBound, upperBound] when it’s included and as open ]lowerBound, upperBound[ otherwise.

Our goal is that our lambda is able to generate another lambda accordingly to the options which could be used as std::count_if predicate:

return [lowerBound, upperBound, including](int val)->bool { ... }

You have already learned how to use the capture list, and you will not be surprised at the use of variables in the formula for calculating the value of the predicate:

((lowerBound < val)&&(val < upperBound))||(including && (val == lowerBound || val == upperBound))

What happens after we assign the result of the generated lambda function to an external predicate variable?

predicate = lambdagen(false);

lambdagen already returned  its result and generated a lambda assigned to the predicate variable. In a functional language Haskell, we are dealing with currying and lazy execution, and it was not anything extraordinary. Here, however, we are dealing with C++ where the instructions are executed strictly sequentially. Thus,  lambdagen internal scope does yet not exist! What do you think, what variables will appear in this formula after the call:

result = count_if(v.begin(), v.end(), predicate);

It's amazing, it works without error. The function is dead, but some of its local variables are still alive! This is called capturing (I would call these “ghost variables” - although Philip van der Decken  was not a programmer, ha-ha!)

Incidentally, the closure allowed us to keep these ghost variables in the ghost state of the ghost object. Within the framework of functional programming, closures allow for simulating the passing of an internal state for subsequent processing.

Note that we actually have a parameterized generation of the lambda function. So we can receive a predicate for the closed interval case:  

predicate = lambdagen(true);

Do not be offended if you have to enter the limits of the range twice—this is inevitable—because they are entered inside the generated lambda every time it’s called.

#pragma hdrstop
#pragma argsused

#ifdef _WIN32
#include <tchar.h>
#else
  typedef char _TCHAR;
  #define _tmain main
#endif
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

using namespace std;


 int _tmain(int argc, _TCHAR* argv[])
{
	int m;

	cout << "Enter array size: ";
	cin >> m;
	vector<int> v(m);
	int k = 0;



	generate(v.begin(), v.end(), [&](){return k++;});

	cout << "Content of array: ";
	for_each(v.begin(), v.end(), [](int value){ cout << value << " "; });
	cout << endl;



	function<bool(int)> predicate;

	auto lambdagen =
		[](bool including)->function<bool(int)>
		{
			int lowerBound = 0, upperBound = 0;
			cout << "Enter the bounds of the range: ";
			cin >> lowerBound >> upperBound;
			return [lowerBound, upperBound, including](int val)->bool
			{
				return ((lowerBound < val)&&(val < upperBound))||(including && (val == lowerBound || val == upperBound));
			};
		};



	int result;

	predicate = lambdagen(false);
	result = count_if(v.begin(), v.end(), predicate);
	cout << "Number of elements in the open interval: " << result << endl;


	predicate = lambdagen(true);
	result = count_if(v.begin(), v.end(), predicate);
	cout << "Number of elements in the closed interval: " << result << endl;

	cin.ignore(2);
	return EXIT_SUCCESS;
}

C++ Builder __closure keyword

Our presentation could not be considered complete without mention of the keyword __closure, which appeared in Borland C++ Builder at the turn of the century, long before we could even dream of the functional features of C++ in a serious way.

A keyword ahead of its time

A function pointer allows make reference to a global function and call it when necessary. However, for Class member functions (methods) it is not sufficient because two pointers are required: one for the originating object and the second for the method address. There is a need to create "2-in-1" pointers to methods and the __closure keyword allows us perform that task.

 

Role in the VCL

When building a UI, instead of manually creating each window element, you use a graphical designer and then just write the handler for the event you are interested in. How can this be implemented in practice? Since you use standard window elements, you need to override the behavior of such a component for a particular event, i.e. create its descendant, as it’s done in classic OOP.

Suppose we have 2 buttons. They are two instances of the same TButton class that have a different behaviors. The first one prints “Yes” when pressed, second one prints “No.” In the case of the traditional OOP approach with the absence of delegation, the solution would be to create classes TButtonYes and TButtonNo with a redefined behavior at the click event, then create one instance of each class. This is contrary to our intuition that there is a single class, and that there are the discrepant instances of that class only!

VCL opened up another pathway here offering an elegant solution. Instead of defining behavior in the class for each visual component in your application, just delegate the processing of the relevant event to the interested class. Therefore, the buttons always remain just buttons, regardless of what kind of work the handler of their click performs!

It sounds promising, but how do you implement it in practice if the VCL developer does not know which class will delegate the right to process the click? Note, “pure” C++ only had plain function pointers, not object-method pointers. At the same time the delegate here is the class method and it is the pointer to the class member-function that is required.

Look at the following definition:

typedef void __fastcall (__closure *TNotifyEvent)(System::TObject* Sender);

This is the type of the event notification signature. Sender contains a pointer to the object in which the event occurred (because there are many buttons that can be presented on the form), in order for the delegate to have access to the properties and methods of the object. It represents a kind of feedback while TNotifyEvent is the declared type whose variable can contain a reference to a delegate. Thus, if a visual component has a property of TNotifyEvent type, it can be assigned a reference to a handler, which is a method of the interested class that will be executed as soon as the notification of the event that has occurred is received.

The declaration of the member-function-pointer with the __closure keyword allows to delegate the implementation of the member-function to any arbitrary class, rather than a descendant of a specific one.

Syntax

typedef <type> ( __closure * <id> ) (<param list>);

where

id - the identifier of the pointer to the function-member of some class

Examples

Let's compare what the VCL application would look like with and without __closure. To show this we will create a small demo project. Please, create an empty VCL Forms application as shown:

 

After it put TMemo component on the form.

First, we try to create two buttons which will print “Yes” or “No” when pressed. Our goal is to achieve this without using VCL event mechanisms (onClick property).

Nevertheless, we do not need to implement self-handling messages from the mouse.  We have already a parent class TButton in our hands with implemented dynamic Click() method which called every time when left mouse key pressed. Normally, it perform some additional actions and call handler assigned to TButton::onClick property.  But taking into account our obligations, we can override his standard behavior for each of the required cases (printing “Yes” and printing “No”) .

So we had to create two separate classes TButtonYes and TButtonNo with behavior accordingly to our task:

class TButtonYes : public TButton{

public: virtual __fastcall  TButtonYes(TComponent* Owner) : TButton(Owner)

{

Caption = L"Yes";

}

 

void __fastcall printYes()

{

((TForm1*)(GetParentForm(this, true)))->Memo1->Lines->Add(L"Yes"); // this->Parent

 

}

 

DYNAMIC void __fastcall Click(void)

{

printYes();

}

};

 

class TButtonNo : public TButton{

public: virtual __fastcall  TButtonNo(TComponent* Owner) : TButton(Owner)

{

Caption = L"No";

}

 

void __fastcall printNo()

{

((TForm1*)(GetParentForm(this, true)))->Memo1->Lines->Add(L"No");

 

}

DYNAMIC void __fastcall Click(void)

{

printNo();

}

};

As you can see, Click() method is overridden thus to call the member function print.. of the button class for processing instead of passing the execution to the delegate specified in the onClick property as doing by default. In addition, the size and location of the each button on the TForm1 form are described here, since we will create it dynamically at run-time.

Now we can declare pointers to these two buttons objects:

TButton *ButtonYes;

TButton *ButtonNo;

Next can double-click in the main form and put followed code in the constructor of the TForm1:

ButtonYes = new TButtonYes(this);

ButtonNo = new TButtonNo(this);

and (remember, our buttons are created in runtime!) determine the place that these components will occupy on the form:

ButtonYes->Top = 48;

ButtonYes->Left = 8;

ButtonYes->Parent = this;

ButtonNo->Top = 48;

ButtonNo->Left = 88;

ButtonNo->Parent = this;

 

Finally, you can run the application now and see the results.

Again: we did what we had to do within the framework of the concept of "pure" OOP, without resorting to the use of delegation. Quite a lot of code for the task of creating just two different buttons, isn’t it?

But You can use the OnClick property in a much more convenient way and __closure keyword allows for it. You can create new project, but to feel the difference let's continue working with this project.

Keep in mind! The following example may seem confusing if you just read it. But in order to understand how it works it will only need to compile it and try to click on the buttons. Our goal now is to demonstrate that the __closure keyword makes it easy to juggle with ease button handlers in runtime.

Handlers will change places, but at some point they will need to return to their original state. Signatures on the surface of buttons are initialized by their names, this is done for better remembering by the user who launched the program, but does not press anything. These names change during operation and do not return to their original state.

Place two TButton’s onto the form (not at the same place where ButtonYes and ButtonNo appear, if you are continue previous example) and from the Events tab double-click on the ‘OnClick’ property field. Thus, TForm1::Button1Click and TForm1::Button2Click handlers templates will be created.

Type the following code for this handlers:

void __fastcall TForm1::Button1Click(TObject *Sender)

{

Form1->Memo1->Lines->Add(L"========== "+((TButton*) Sender)->Caption + L" click ==========");

Form1->Memo1->Lines->Add(L"Sender: ''"+((TButton*) Sender)->Name+L"''"

+L"\t Handler: ''TForm1::Button1Click''");

 

((TButton*) Sender)->OnClick = &delegate.ExternalClickHandler;

((TButton*) Sender)->Caption = L"*";

Form1->Memo1->Lines->Add(((TButton*) Sender)->Name+L"->OnClick property was now assigned to external handler");

Form1->Memo1->Lines->Add(L"----------------------------------------------------");

}



and

 

void __fastcall TForm1::Button2Click(TObject *Sender)

{

Form1->Memo1->Lines->Add(L"========== "+((TButton*) Sender)->Caption + L" click ==========");

Form1->Memo1->Lines->Add(L"Sender: ''"+((TButton*) Sender)->Name+L"''"

+L"\t Handler: ''TForm1::Button2Click''");



((TButton*) Sender)->OnClick = &delegate.ExternalClickHandler;

((TButton*) Sender)->Caption = L"*";

Form1->Memo1->Lines->Add(((TButton*) Sender)->Name+L"->OnClick property was now assigned to external handler");

Form1->Memo1->Lines->Add(L"----------------------------------------------------");

}

If you open the .dfm file now you can right-click on the form and select “View as Text” or save project changes and open .dfm in any text viewer. You should see something like the following:

object Button1: TButton

   Left = 8

   Top = 16

...

   OnClick = Button1Click

 end

 object Button2: TButton

   Left = 104

   Top = 16

   ...

   OnClick = Button2Click

 end

You can see that the OnClick handler is assigned to the TForm1 method automatically and it’s much easy now to create a derived class of button and override its behavior, isn’t it? But don’t be confused that you can only use VCL component methods as handlers. Let's create our own class, whose method can act as a delegate:

class Delegator{

public:

void _fastcall ExternalClickHandler(TObject *Sender)

{

Form1->Memo1->Lines->Add(L"========== "+((TButton*) Sender)->Caption + L" click ==========");

Form1->Memo1->Lines->Add(L"Sender: ''"+((TButton*) Sender)->Name+L"''"

+L"\t Handler: ''Delegator::ExternalClickHandler''");

 

if  (Form1->Button1->OnClick == Form1->Button2->OnClick)

{

Form1->Memo1->Lines->Add(L"Restore click handlers...");

 

Form1->Button1->OnClick = Form1->Button1Click;

Form1->Button2->OnClick = Form1->Button2Click;

 

Form1->Button1->Caption = L"YES";

Form1->Button2->Caption = L"NO";

 

Form1->Memo1->Lines->Add(L"From Delegator: OnClick properties was restored to default Form handlers");

}

else

{

Form1->Memo1->Lines->Add(L"Exchange click handlers...");

 

TNotifyEvent event;

event =  Form1->Button1->OnClick;

Form1->Button1->OnClick = Form1->Button2->OnClick;

Form1->Button2->OnClick = event;

 

UnicodeString buf = Form1->Button1->Caption;

Form1->Button1->Caption = Form1->Button2->Caption;

Form1->Button2->Caption = buf;

}

 

Form1->Memo1->Lines->Add(L"----------------------------------------------------");

}

};

 

and declare its instance:

Delegator delegate;

Our handlers operate with pointers to delegates in a completely arbitrary style. Let's try to understand the functionality of this code.

We have two handlers TForm1::Button1Click and TForm1::Button2Click which acts likely way. But notice that matching the names of the handlers to the names of the components whose events they process are not necessarily! Generally speaking, this restriction is dictated only by common sense, but if necessary we can manipulate it arbitrarily.

First, prints to the log  the caption of the button that was pressed. Second, prints  the Name of the button - this is the only thing that always remains in place. Third, external handler assigns to the button onClick property and finally button caption changes to the “*” symbol.

Now a few words about the external handler. It is a ExternalClickHandler method of the Delegator class. When called it prints  the caption of the button that was pressed into the log too. Next it compare the handlers of the buttons Button1::OnClick and Button2::OnClick both. If it is the same handler (i.e. ExternalClickHandler because it is not called otherway) then both handlers was changed from initial and it restores the init values and captions. Else one button has external handler for onClick property and ExternalClickHandler exchange handlers (and captions) of the Button1::OnClick and Button2::OnClick.

In a sense, this task resembles the next chain of states:

Each state includes the values of the caption (signed on the button surface) and handler (in the rectangle from below) for each of the buttons Button1 (on the left) and Button2 (on the right). For compactness the handlers TForm1::Button1Click and TForm1::Button2Click are signed as Button1Click and Button2Click respectively, and the Delegator::ExternalClickHandler handler is designated as external.

Note that the chain of states is not closed, and the transitions shown in the following figure are possible from the lower state:


Thus, the second state diagram can be viewed as a continuation of the first, but if you initialize the Button1 and Button2 button captions as "YES" and "NO" respectively, the top diagram will turn into the bottom one. You can do this by changing the Caption property of both buttons at the design stage, or at runtime in the TForm1 constructor. I saved the default button names in order to remember the location of the components TForm1::Button1 and TForm1::Button2 (but note that this values can be exchanges and in some states Button1 has caption “Button2”).

It's not so difficult to understand if you play with this program yourself.

Now we can try to reproduce the following log:

 

Yes

No

========== Button1 click ==========

Sender: ''Button1'' Handler: ''TForm1::Button1Click''

Button1->OnClick property was now assigned to external handler

----------------------------------------------------

========== * click ==========

Sender: ''Button1'' Handler: ''Delegator::ExternalClickHandler''

Exchange click handlers...

----------------------------------------------------

========== Button2 click ==========

Sender: ''Button1'' Handler: ''TForm1::Button2Click''

Button1->OnClick property was now assigned to external handler

----------------------------------------------------

========== * click ==========

Sender: ''Button2'' Handler: ''Delegator::ExternalClickHandler''

Restore click handlers...

From Delegator: OnClick properties was restored to default Form handlers

----------------------------------------------------

========== NO click ==========

Sender: ''Button2'' Handler: ''TForm1::Button2Click''

Button2->OnClick property was now assigned to external handler

----------------------------------------------------

========== * click ==========

Sender: ''Button2'' Handler: ''Delegator::ExternalClickHandler''

Exchange click handlers...

----------------------------------------------------

========== YES click ==========

Sender: ''Button2'' Handler: ''TForm1::Button1Click''

Button2->OnClick property was now assigned to external handler

----------------------------------------------------

========== * click ==========

Sender: ''Button2'' Handler: ''Delegator::ExternalClickHandler''

Restore click handlers...

From Delegator: OnClick properties was restored to default Form handlers

----------------------------------------------------

 

First, we clicked created at run-time “Yes” and “No” buttons for which we has implemented the  different behaviors using inheritance  instead delegation mechanism, as you remember. Next, we can press a sequence of buttons corresponding to one of the paths in the state graph.

After it the main form of our application will looks as follows:

 I'm forced to bring here the contents of the header file: this way in case something goes wrong - so that you could understand the cause of the problem.

Unit1.h:

//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE-managed Components
	TMemo *Memo1;
	TButton *Button1;
	TButton *Button2;
	void __fastcall Button1Click(TObject *Sender);
	void __fastcall Button2Click(TObject *Sender);
private:	// User declarations
public:		// User declarations
	__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

 

The full content of the Unit1.cpp source is:

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

class TButtonYes : public TButton{
	public: virtual __fastcall  TButtonYes(TComponent* Owner) : TButton(Owner)
	{
		Caption = L"Yes";
	}

	 void __fastcall printYes()
	{
		((TForm1*)(GetParentForm(this, true)))->Memo1->Lines->Add(L"Yes");

	}

	DYNAMIC void __fastcall Click(void)
	{
		printYes();
	}
};

class TButtonNo : public TButton{
	public: virtual __fastcall  TButtonNo(TComponent* Owner) : TButton(Owner)
	{
		Caption = L"No";
	}

	 void __fastcall printNo()
	{
		((TForm1*)(GetParentForm(this, true)))->Memo1->Lines->Add(L"No");

	}
	DYNAMIC void __fastcall Click(void)
	{
		printNo();
	}
};

TButton *ButtonYes;
TButton *ButtonNo;
//----------------------------------------------------------------------
class Delegator{
	public:
	void _fastcall ExternalClickHandler(TObject *Sender)
	{
		Form1->Memo1->Lines->Add(L"========== "+((TButton*) Sender)->Caption + L" click ==========");
		Form1->Memo1->Lines->Add(L"Sender: ''"+((TButton*) Sender)->Name+L"''"
								+L"\t Handler: ''Delegator::ExternalClickHandler''");

		if  (Form1->Button1->OnClick == Form1->Button2->OnClick)
		{
			Form1->Memo1->Lines->Add(L"Restore click handlers...");

			Form1->Button1->OnClick = Form1->Button1Click;
			Form1->Button2->OnClick = Form1->Button2Click;

			Form1->Button1->Caption = L"YES";
			Form1->Button2->Caption = L"NO";

			Form1->Memo1->Lines->Add(L"From Delegator: OnClick properties was restored to default Form handlers");
		}
		else
		{
			Form1->Memo1->Lines->Add(L"Exchange click handlers...");

			TNotifyEvent event;
			event =  Form1->Button1->OnClick;
			Form1->Button1->OnClick = Form1->Button2->OnClick;
			Form1->Button2->OnClick = event;

			UnicodeString buf = Form1->Button1->Caption;
			Form1->Button1->Caption = Form1->Button2->Caption;
			Form1->Button2->Caption = buf;
		}

		Form1->Memo1->Lines->Add(L"----------------------------------------------------");
	}
};

Delegator delegate;


//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
	ButtonYes = new TButtonYes(this);
	ButtonYes->Top = 48;
	ButtonYes->Left = 8;
	ButtonYes->Parent = this;//!(TForm1*)(Owner);

	ButtonNo = new TButtonNo(this);
	ButtonNo->Top = 48;
	ButtonNo->Left = 88;
	ButtonNo->Parent = this;//!(TForm1*)(Owner);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
	Form1->Memo1->Lines->Add(L"========== "+((TButton*) Sender)->Caption + L" click ==========");
	Form1->Memo1->Lines->Add(L"Sender: ''"+((TButton*) Sender)->Name+L"''"
								+L"\t Handler: ''TForm1::Button1Click''");

	((TButton*) Sender)->OnClick = &delegate.ExternalClickHandler;
	((TButton*) Sender)->Caption = L"*";
	Form1->Memo1->Lines->Add(((TButton*) Sender)->Name+L"->OnClick property was now assigned to external handler");
	Form1->Memo1->Lines->Add(L"----------------------------------------------------");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
	Form1->Memo1->Lines->Add(L"========== "+((TButton*) Sender)->Caption + L" click ==========");
	Form1->Memo1->Lines->Add(L"Sender: ''"+((TButton*) Sender)->Name+L"''"
								+L"\t Handler: ''TForm1::Button2Click''");


	((TButton*) Sender)->OnClick = &delegate.ExternalClickHandler;
	((TButton*) Sender)->Caption = L"*";
	Form1->Memo1->Lines->Add(((TButton*) Sender)->Name+L"->OnClick property was now assigned to external handler");
	Form1->Memo1->Lines->Add(L"----------------------------------------------------");
}
//---------------------------------------------------------------------------

 In addition, you can compare the resulting file of your form with the following:

object Form1: TForm1
  Left = 0
  Top = 0
  BorderStyle = bsSingle
  Caption = 'Form1'
  ClientHeight = 498
  ClientWidth = 603
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -13
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 120
  TextHeight = 16
  object Memo1: TMemo
    Left = 181
    Top = 0
    Width = 422
    Height = 498
    Align = alRight
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -10
    Font.Name = 'Tahoma'
    Font.Style = []
    Lines.Strings = (
      '')
    ParentFont = False
    ScrollBars = ssVertical
    TabOrder = 0
  end
  object Button1: TButton
    Left = 8
    Top = 8
    Width = 75
    Height = 25
    Caption = 'Button1'
    TabOrder = 1
    OnClick = Button1Click
  end
  object Button2: TButton
    Left = 88
    Top = 8
    Width = 75
    Height = 25
    Caption = 'Button2'
    TabOrder = 2
    OnClick = Button2Click
  end
end

 

After experimenting with this code, you will not think about VCL event properties as something mysterious. Despite that, it still may not obvious what role the __closure mechanism plays here since it is hidden inside the TNotifyEvent type definition. Perhaps its place is only inside VCL?

Let's assume that we again need to count the number of elements of a vector in a certain range. We have a hierarchy of classes representing the base interval Diapasoner, subtypes of this interval such as open OpenInterval, and closed ClosedInterval.

The Diapasoner class has a virtual CheckCondition member-function which always returns true and must be overridden in derived classes.

 

How can I use its  member-function as predicate in the std::count_if algorithm?

We can try to assign a method address to a pointer to a global function, but this does not work of course:

bool (*predicate)(int);//predicate is a pointer to a global function

// predicate = &Diapasoner::CheckCondition;

// [bcc32c Error] File1.cpp(89): assigning to 'bool (*)(int)' from incompatible type 'bool (Diapasoner::*)(int)'

Next, we can create an object of the Diapasoner type and use its trivially defined CheckCondition  method:

bool (Diapasoner::*conditfun)(int); //conditfun is a member-function pointer

conditfun = &Diapasoner::CheckCondition;

This type of pointer can be used for subclasses:

intervalObj = new ClosedInterval(lowerBound, upperBound);

conditfun = &Diapasoner::CheckCondition;

We cannot use this variable to point to member-functions declared in subclasses or in third-party classes:

conditfun = &ClosedInterval::CheckCondition;

compile-time error raised: “assigning to 'bool (Diapasoner::*)(int)' from incompatible type 'bool (ClosedInterval::*)(int)': different classes ('Diapasoner' vs 'ClosedInterval')

OR

conditfun = &ClosedInterval::inClosed;

failed too due Diapasoner class have no inClosed method so conditfun cannot to point to it;

OR

bool (ClosedInterval::*conditfun_)(int); //declare conditfun_ as a member-function pointer to methods of the ClosedInterval class

...

conditfun_ = &OpenInterval::CheckCondition;

compile-time error raised: “assigning to 'bool (ClosedInterval::*)(int)' from incompatible type 'bool (OpenInterval::*)(int)': different classes ('ClosedInterval' vs 'OpenInterval')

Naturally, all these methods did not work, because we must monitor not only the coincidence of the method call signature but also the compatibility of the classes to which these methods belong. I.e. the child-class possesses the necessary information about its ancestor, however, it can not know anything about the internal arrangement of its descendants, brothers or even more unrelated classes.

 

But, perhaps, I can use a pointer to the member function directly in the STL call? All I need is to have a declared pointer to the member function of my class

Unfortunately, everything turned out to be different from the ideal world. When I try to compile

result = count_if(v.begin(), v.end(), intervalObj->*conditfun );

I receive a rather strange "internal compiler error." I did not come up with an exit better than using a ordinary loop for counting:

result = 0;

for (int val: v)

{

(intervalObj->*conditfun)(val) ? result++ : result=result;

}

 

I want to show how __closure offers us a more common solution: it is type-compatible based on the method signature not the class type - unlike, say, std::bind. In fact, why think about the type of object if all I want to focus on its member function? It would be cool, if in some cases we could work on the method, not on the class type, which is how it can work as a delegate.

Let’s declare the next variable:

bool (__closure *predicateX)(int value);

 

Now we can assign to it the method of any class, but only if  this method has the bool (int value) call signature:

predicateX = intervalObj->CheckCondition;

and in another place later reassign predicateX variable:

interval0Obj = (OpenInterval*)intervalObj;

predicateX = interval0Obj->inHalfOpenLeft;

 

And, of course, you are free to use it in std::algorithm

result = count_if(v.begin(), v.end(), predicateX);

 

The trick is that the predicateX variable now contains the pair (&intervalObj, &CheckCondition), so it's enough data to recover the member-function within their object inside count_if.

 

Note, if this statement is taken out of context you can not determine from the object which method will be called here. This is not a std::function, but without going beyond the boundaries of the OOP it provides the flexibility: you can save a single pointer to the class method along with the class itself, which saves the headache of determining a specific pointer for each class.

#pragma hdrstop
#include <iostream>
#include <vector>
#include <algorithm>
#pragma argsused

#ifdef _WIN32
#include <tchar.h>
#else
  typedef char _TCHAR;
  #define _tmain main
#endif

using namespace std;


class Diapasoner
{
	protected:
		int _a;
		int _b;
	public:
		virtual bool CheckCondition(int n) { return true; };
		Diapasoner(int a, int b)
		{
			_a = a;
			_b = b;
		}
};


class ClosedInterval : public Diapasoner{
	public: ClosedInterval (int a, int b)  : Diapasoner(a, b) {}
		virtual bool CheckCondition(int n)
		{
			return inClosed(n);
		}

		bool inClosed(int n)
		{
			return (_a <= n) && (n <= _b);
		}

};

class OpenInterval : public Diapasoner{
	public: OpenInterval (int a, int b)  : Diapasoner(a, b) {}

		virtual bool CheckCondition(int n)
		{
			return  inOpen(n);
		}
		bool inOpen(int n)
		{
			return (_a < n) && (n < _b);
		}

		bool inHalfOpenRight(int n)
		{
			return (_a <= n) && (n < _b);
		}
		bool inHalfOpenLeft(int n)
		{
			return (_a < n) && (n <= _b);
		}
};

 int _tmain(int argc, _TCHAR* argv[])
 {

	int m;
	cout << "Enter array size: ";
	cin >> m;
	vector<int> v;

	for (int val = 0; val < m; v.push_back(val++));

	cout << "Received array: ";
	for_each(v.begin(), v.end(), [v](int value){ cout << value << " ";});
	cout << endl;


	int lowerBound = 0, upperBound = 0;
	cout << "\nEnter the borders of the range: ";
	cin >> lowerBound >> upperBound;
    cout << endl;
	int result = 0;

	bool (*predicate)(int);
//	predicate = &Diapasoner::CheckCondition;
// [bcc32c Error] File1.cpp(83): assigning to 'bool (*)(int)' from incompatible type 'bool (Diapasoner::*)(int)'

	bool (Diapasoner::*conditfun)(int);
	conditfun = &Diapasoner::CheckCondition;


	Diapasoner *intervalObj = new Diapasoner(lowerBound, upperBound);

	result = 0;
	for (int val: v)
	{
		(intervalObj->*conditfun)(val) ? result++ : result=result;
	}

	cout << "Number of elements in diapason " << lowerBound << ".." << upperBound << " and outside" << endl;
	cout << "Calculated with base class member-function pointer: " << result << endl;
	cout << endl;
	delete intervalObj;
 //------------------------------------------------------------
	intervalObj = new ClosedInterval(lowerBound, upperBound);
	conditfun = &Diapasoner::CheckCondition;
	result = 0;
	for (int val: v)
	{
		(intervalObj->*conditfun)(val) ? result++ : result=result;
	}

	cout << "Number of elements in diapason [" << lowerBound << ", " << upperBound << "] and outside " << endl;
	cout << "Calculated with derived class overrided member-function pointer: " << result << endl;
	cout << endl;
	delete intervalObj;
 //------------------------------------------------------------
 // try...
	intervalObj = new ClosedInterval(lowerBound, upperBound);
//	conditfun = &ClosedInterval::CheckCondition;
// [bcc32c Error] File1.cpp(115): assigning to 'bool (Diapasoner::*)(int)' from incompatible type 'bool (ClosedInterval::*)(int)': different classes ('Diapasoner' vs 'ClosedInterval')

//	conditfun = &ClosedInterval::inClosed;

	ClosedInterval* interval_Obj = new ClosedInterval(lowerBound, upperBound);
	bool (ClosedInterval::*conditfun_)(int);
	conditfun_ = &ClosedInterval::CheckCondition;
	result = 0;
	for (int val: v)
	{
		(interval_Obj->*conditfun_)(val) ? result++ : result=result;
	}

	cout << "Number of elements in diapason [" << lowerBound << ", " << upperBound << "]" << endl;
	cout << "Calculated with ClosedInterval class member-function pointer: " << result << endl;
	cout << endl;
	delete  interval_Obj;
//---------------------------------------------------

//try...
	OpenInterval* interval0Obj = new OpenInterval(lowerBound, upperBound);
//	conditfun_ = &OpenInterval::CheckCondition;
//[bcc32c Error] File1.cpp(137): assigning to 'bool (ClosedInterval::*)(int)' from incompatible type 'bool (OpenInterval::*)(int)': different classes ('ClosedInterval' vs 'OpenInterval')

//----------------------------------------------------
	bool (__closure *predicateX)(int value);


	intervalObj = new Diapasoner(lowerBound, upperBound);
	predicateX = intervalObj->CheckCondition;
	result = 0;
	for (int val: v)
	{
		predicateX(val) ? result++ : result=result;
	}
	cout << "Number of elements in diapason " << lowerBound << ".." << upperBound << " and outside" << endl;
	cout << "Calculated for base Diapasoner object with predicateX pointer (trivial): " << result << endl;
	cout << endl;
	delete intervalObj;

	intervalObj = new ClosedInterval(lowerBound, upperBound);


	predicateX = intervalObj->CheckCondition;
	result = 0;
	for (int val: v)
	{
		predicateX(val) ? result++ : result=result;
	}
	cout << "Number of elements in diapason [" << lowerBound << ", " << upperBound << "]" << endl;
	cout << "Calculated for ClosedInterval object with predicateX pointer: " << result << endl;
	cout << endl;
	delete intervalObj;

	intervalObj = new OpenInterval(lowerBound, upperBound);
	predicateX = intervalObj->CheckCondition;
	result = 0;
	for (int val: v)
	{
		predicateX(val) ? result++ : result=result;
	}
	cout << "Number of elements in diapason (" << lowerBound << ", " << upperBound << ")" << endl;
	cout << "Calculated for OpenInterval object with predicateX pointer: " << result << endl;
	cout << endl;

	interval0Obj = (OpenInterval*)intervalObj;
	predicateX = interval0Obj->inHalfOpenLeft;
	result = count_if(v.begin(), v.end(), predicateX);

	cout << "Number of elements in half-open diapason (" << lowerBound << ", " << upperBound << "]" << endl;
	cout << "Calculated in count_if for OpenInterval class with predicateX pointer: " << result << endl;

	predicateX = interval0Obj->inHalfOpenRight;
	result = count_if(v.begin(), v.end(), predicateX);

	cout << "Number of elements in half-open diapason [" << lowerBound << ", " << upperBound << ")" << endl;
	cout << "Calculated in count_if for OpenInterval class with predicateX pointer: " << result << endl;
	cout << endl;

	delete intervalObj;

	std::cin.ignore(2);
	return EXIT_SUCCESS;

 }



 

Perhaps, the presentation of this material seemed somewhat vague to you, and examples were far-fetched. Indeed, you can use VCL, and without even thinking about the details of the implementation of TNotifyEvent, and for storing of the function-members, we now have std::function. Nevertheless, if you want to feel the essence of the problem of working with pointers to class methods, I recommend you read Mohammad Nasim’s article where this material is presented very talently and succinctly:  “C++ Delegates and Borland's C++ Builder Event Handling” -- Part I and Part II.

 

Fun fact: Borland was forced to use a functional style approach in C++Builder VCL by introducing the use of a __closure keyword to create the infrastructure of a classical object-oriented language (such as C++) many years before the functional elements were included in standard! Something similar (in conceptual sense) is happening now with the possibility of using functors and lambda expressions together with the algorithms of STL. This suggests that although you can never write programs purely in the functional paradigm using C++ (because it’s an imperative language and not intended for such a thing) functional elements can enhance the capabilities of object-oriented languages and embellish them.

 


Read More

【Delphi / C++Builder】Starter無償提供1周年記念!アプリケーション開発の基礎が学べるWebセミナー(1限目)[JAPAN]

$
0
0

Delphi / C++Builder Starter チュートリアルシリーズ 】
Starter無償提供1周年記念!‟アプリケーション開発の基礎„

2017年9月22日(金)17:00より、Delphi/C++Builder Starter 無償提供開始から1周年を記念して、Starterチュートリアルシリーズ の特別編「アプリケーション開発の基礎」Webセミナーを実施しました。この記事では そのWebセミナーの1限目部分について、記載&補足してまいります。

無料版 Starter Edition 10.2 Tokyo Release 1 入手の方法

無料で使える Windowsアプリ開発環境の Delphi / C++Builder Starter 、まだ入手されていない方はエンバカデロテクノロジーズ社のWebページから上部にメニューバーらしきところに表示されている「無料版」からDelphi Starter もしくは C++Builder を選択して入手してください。
入手、インストールの詳細を知りたい方は、以前の記事をご参照ください。※すでにインストール済ではあるものの、新しい10.2.1版に更新したいという方は、こちらをご参照ください

セミナー動画をオンデマンドで公開中

セミナーのビデオを公開しています。下記URLより視聴申し込みを行うと無料でご覧いただけます。

アジェンダ:アプリケーション開発の基礎が学べる

実施内容は下記3部に分け、Q&A形式で進めました。
image.png

1限目 :「データ入力、保存、出力を学ぶ」

1限目のQ&Aの内容は下記の通り。順にご説明してまいります。

  • 片手でデータ入力できるようにしたい!
  • 入力されたデータを一覧表示したいのだけど…?
  • 作ったデータを保存したいのですが…?

[Q] 片手でデータ入力できるようにしたい!

タブレットデバイスなどのアプリを作って野外利用する、そんなケースでは、キーボードはありません。そんな用途が考えられるアプリの場合、キーボードなく、片手で入力できるようにするには…

以前のStarterチュートリアルではTEditを入力コンポーネントとして紹介しましたが、これはキーボードによる入力を必要としていました。

そんなときにはタップやクリックなどで入力する方法を使いましょう。例えば複数選択肢からクリック、タップで値を選択できるコンボボックス(TComboBox) や、矢印の上下ボタンをクリップ、タップすることで数値を入力できるスピンエディット(TSpinEdit)を使ってみましょう。 
image.png

入力されたデータを一覧表示したいのだけど…?

入力方法は思いついた!でもその結果を見やすく表示したい…そんな時、どうしましょうか?

以前のチュートリアルシリーズではテキスト情報として複数表示するならTMemoを紹介しましたが、一覧表示するだけでデータとして活用するには物足りない!

そんなときは、入力データをデータベースのように扱って表示してみましょう!入力したデータをまとめて「データセット」として保持することでデータセットを表として出力すれば見やすくきれいに表示できます。TClientDataSetにデータを保存し、TDataSouceにデータを渡してデータ配布してもらう中継ぎをしてもらい、TDBGridに繋ぎ渡して一覧表示します。

※TClientDataSetの使い方、詳しく記載したブログ@CYonezawaさんによって記載されています。

ちなみにセミナーでコードを使ってTClientDataセットにレコード登録していたときのコードは下記の通り

   begin
      //データセットをオープン(最初に念のためいったんクローズしてから開く)
      ClientDataSet1.Close;
      ClientDataSet1.Open;
      //Append することによりデータセットの最後にレコード追加状態に
      ClientDataSet1.Append;
      //データセットに用意したフィールドにテキストを代入 
      ClientDataSet1yard.AsString := seDist.Text + 'ヤード';
      ClientDataSet1hole.AsString := cbHole.Text;
      //追加したデータをPostで確定
      ClientDataSet1.Post;
   end;

補足:WebセミナーでTClientDataSetを使うときにはMidas.dllも実行ファイルと一緒に置く必要ありとご紹介しましたが、すみません。StarterではMidas.dllは再配布可能の扱いではありませんでした。Starterのインストール環境にはMidas.dllが存在しますのでTClientDataSetを使用して動作させることができます。しかしStarterがインストールされていなければ動作させることはできない、ということになります。訂正してお詫び申し上げます。

[Q]作ったデータを残しておきたいのです…

よろしい、ならば保存だ! テキスト、CSVなら「TStringList」クラスが保存、ならびに読み込にも便利に使えます。一方、先ほど使ったデータセットとして保存、読み込みするなら、もっと簡単に行えます。

  • TClientDataSet.SaveToFile メソッドを使用しましょう。
  • ファイルの保存場所を問うダイアログを開くならTSaveDialogが使えます。テキスト形式での保存のためのダイアログなら TSaveTextDialogも良いです。
  • ファイル保存のデフォルト場所にもこだわりたいです。一時的な保存場所ならTempフォルダ、テキスト等ならドキュメントフォルダに、保存したいですよね。そんな時、System.IOUtils.TPath を使えば、ドキュメントフォルダやTempフォルダがどこにあるか、取得することができます。

保存の際に使用したコード

//ログ保存
procedure TForm1.btnSaveClick(Sender: TObject);
begin
  //ドキュメントフォルダのパスを取得して保存場所ダイアログを開いた時のデフォルトパスに設定
  SaveDialog1.InitialDir := TPath.GetDocumentsPath;
  //保存ダイアログ開いて...
  if SaveDialog1.Execute then
    begin
      //ファイルの保存場所を引数としてファイル形式をXMLUTF8で保存
      ClientDataSet1.SaveToFile(SaveDialog1.FileName, dfXMLUTF8);
    end;
end;

ちなみに読み込み側はこのような感じで使えます。

//ログ読込
procedure TForm1.btnLoadClick(Sender: TObject);
begin
//  //ログファイル読込のデフォルトパスを設定
  OpenDialog1.InitialDir :=  TPath.GetDocumentsPath;
  //読込みダイアログをひらいて
  if OpenDialog1.Execute then
    begin
      ClientDataSet1.LoadFromFile(OpenDialog1.FileName);
    end;
end;

1限目まとめ

  • 片手で入力できるコンポーネントTComboBox, TSpinEditでキーボードなしで入力する方法をご紹介しました
  • 入力データを一覧表示する方法として、TClientDataSetと TDataSource、TDBGridを活用してデータベースのようにデータ一式を表示する方法をご紹介しました
  • データ保存の方法としてをデータセットの保存法TClientDataSet.SaveToFile、保存場所のダイアログをTSaveDialogで、表示、特定のパス情報をSystem.IOUtils.TPathで取得する方法をご紹介しました

クラウドや他のDBに保存しているデータを取得し、表示させたい、そんなときは・・・

Professional版以上のエディションが最適!Delphi/C++Builderには、Starter、Professional、Enterprise、Architectの4つのエディションがあります。

最近はクラウドなど、ローカル以外にデータを保存することが増えてきました。
より便利に、使い勝手のよいアプリケーション開発を行うなら、Professional 版以上のライセンスが最適です。

期間限定! Starterから上位エディションへアップグレードするチャンス! Delphi発売22周年記念キャンペーン開催中!通常よりもおよそ20% OFFの特別価格で上位エディションをお求めいただけます。

  • 対象期間:2017年9月12日~2017年9月27日
  • 対象者:Delphi Starter EditionまたはC++Builder Starter Editionユーザー(製品登録が必要)
  • 購入先:Eショップ限定(ComponentSource、翔泳社SEshop、RIOS eshop) ※キャンペーン最終日の終了時間は、ショップにより異なります。 各Eショップの案内をご確認ください。
  • 詳しくは:https://www.embarcadero.com/jp/radoffer をご覧ください

しかも!今なら「Delphi 22周年記念冊子」をプレゼント!Delphi誕生からの22年間の“歴史”、そして”未来“が語られている記念冊子。ぜひ皆さまにも手にしていただきたい一冊です。
image.png

  • 対象期間:2017年9月12日~2017年9月27日
  • 対象者:Delphi Starter EditionまたはC++Builder Starter Editionから対象期間内に、上位エディションにアップグレードされた方
  • 登録方法:キャンペーンフォームからご応募ください。(購入した製品のシリアルナンバーが必要です)
  • 詳しくは:http://forms.embarcadero.com/delphi-book-request 

キャンペーンは今月9月27日まで。お忘れなく!


Read More

Amazon DynamoDB with Delphi

【Delphi / C++Builder】Starter無償提供1周年記念!アプリケーション開発の基礎が学べるWebセミナー(2限目)[JAPAN]

$
0
0

Delphi / C++Builder Starter Edition が無償でご利用いただけるようになって、1年が経ちました。そこで無償提供開始1周年を記念して、2017年9月22日(金)に「Delphi / C++Builder Starter チュートリアルシリーズ スペシャル」というWebセミナーを開催いたしました。

今回のWebセミナーは放送時間1時間を3つのセッションに区切って放送いたしましたが、ライブセミナーということでオンタイムにご視聴頂けなかった方もいらっしゃると思います。

そこでこの記事では、2限目の20分間でお送りした「作成したアプリケーションが動かない!?その解決法を学ぶ」の一部を抜粋してご紹介いたします。他の記事へのリンクは、本記事の最後のほうに掲載しております。


Read More

Updating IDE Subversion DLLs to address security issues

$
0
0

Recently a security issue allowing allowing local code execution after visiting a malicious URL was identified in Subversion, git and Mercurial. All three source control systems released a synchronised security update.

In RAD Studio, we use an external installation of both git and Mercurial, which you point the IDE to in the Options dialog. (Tools > Options > Version Control, and then either the git or Mercurial subitems.) Update whichever distribution you use.

However, we do ship Subversion DLLs, which could be affected by this issue. These can be easily updated, and we have internally tested new DLLs against RAD Studio 10.2.1.  You can find full instructions in the readme.txt in the \bin\subversion folder - for me this is C:\Program Files (x86)\Embarcadero\Studio\19.0\bin\subversion .  A rough summary is:

  • We test against builds from collab.net, and use the 32-bit binaries. If you use different binaries, you may end up with different DLL names which the IDE will not recognise. For this post, I downloaded "Subversion 1.9.7 (Windows 32-bit)". Make sure you virus-check or take other appropriate precautions as you would from any website, despite collab.net being well-known.
  • If you overwrite the DLLs in the folder above:
    • Please close the IDE first
    • Back up the existing DLLs before replacing them
  • You can also install to any folder, and redirect the IDE. Do this only if you are confident editing the registry.
    • Please close the IDE first
    • In the registry, navigate to HKEY_CURRENT_USER\Software\Embarcadero\BDS\19.0\Subversion
    • Create a new string value called "SvnDllDir" (without quotes), and set its value to the path you installed the DLLs to. You do not need a trailing slash.

The readme file has full details.

 

 

 


Read More

Building a Google Drive VCL application using Enterprise Connectors

$
0
0

In today's blog post,  I am providing a simple step-by-step tutorial for creating a VCL application that connects to Google Drive using the Google Drive Enterprise Connector component.

The CData FireDAC Component for Google Drive provides an easy-to-use database-like interface for Delphi & C++Builder Apps access to live Google Drive data (Files, Changes, Apps, and more).


Read More

Ext JS, Kitto and uniGUI, Oh My!

$
0
0

As a craftsman, it is important to have a good collection of tools in your toolbelt to address all the different types of projects you encounter. Join us tomorrow for our latest in a series of webinars on integrating Sencha's Ext JS with your Delphi and C++Builder projects with Kitto an uniGUI. These are great solutions for delivering web applications with your favorite development tools!

Thursday is a Deep Dive into Kitto [Register now!]

and you can catch replays fo the Intro to Kitto. More information on ethea.it


[YoutubeButton url='https://youtu.be/qGy06bjN42o']

 


 

Also, catch the uniGUI webinar. Find more information on uniGUI.com


[YoutubeButton url='https://www.youtube.com/watch?v=Nw20k_Cbkxg']
 
What is your favorite web development framework? 

Read More

CodeGuardでRTLを使う場合[JAPAN]

$
0
0

 

 

CodeGuardとは

bcc32 (C++Builder 10.2 Tokyo )で構文規則に違反していないためにコンパイラでは検出されないエラーを通知します。

実行時ライブラリを追跡とマルチスレッド アプリケーションを完全にサポート。

機能
メモリおよびリソースの使用状況確認
関数呼び出しの検証

利用方法

プロジェクトオプションから|C++コンパイラ|デバッグ|
CodeGuardを有効にする = true すると設定されます。

void __fastcall TForm2::Button1Click(TObject *Sender)
{
    int* i = new int[1000];
}

RTLを利用する場合

RTLを利用する場合プロジェクトオプション内の
C++リンカ|動的RTLとリンクする = false 
実行じパッケージ|実行時パッケージを使ってリンク = false
上記の設定を行う必要があります。

TObject*をクリエイトする

void __fastcall TForm2::Button1Click(TObject *Sender)
{
    TObject* o = new TObject();
}

動的RTLとリンクする = false と実行時パッケージを使ってリンク = false を設定する事で、下記の確認ができました。
「 The memory block (0x02723AC0) [size: 8 bytes] was allocated with SysGetMem」


Read More

How to Transfer Images via DataSnap REST Part 1

Viewing all 1683 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>