antarey
antarey

Reputation: 37

Translate code from Delphi to C ++ Builder 10.3

There is a library for parsing.

I added ALXmlDoc.pas to the project, C++Builder created ALXmlDoc.hpp   In ALXmlDoc.pas lines 177,178:  

property Nodes [const Name: AnsiString]: TALXMLNode read GetNodeByName; default;
property Nodes [const Index: integer]: TALXMLNode read GetNodeByIndex; default;

In ALXmlDoc.hpp:  

__property TALXMLNode * Nodes [const System :: AnsiString Name] = {read = GetNodeByName / *, default */};
__property TALXMLNode * Nodes [const int Index] = {read = GetNodeByIndex};

I get an error about duplication - how to fix it?


In .pas line 705

property OnParseText: TAlXMLParseTextEvent read FonParseText Write FonParseText; // [added from TXMLDocument]

In ALXmlDoc.hpp:

__property _di_TAlXMLParseTextEvent OnParseText = {read = FonParseText, write = FonParseText};
__interface TAlXMLParseTextEvent: public System :: IInterface
{
    virtual void __fastcall Invoke (System :: TObject * Sender, const System :: AnsiString Path, const System :: AnsiString Str) = 0;
};
     
private:
    _di_TAlXMLParseTextEvent FonParseText;
protected:
    void __fastcall DoParseText (const System :: AnsiString Path, const System :: AnsiString Str);

  In my Unit1.h:  

void __fastcall OnParseText (System :: TObject * Sender, const System :: AnsiString Path, const System :: AnsiString Str);

In my Unit1.cpp:  

void __fastcall TForm1 :: OnParseText (System :: TObject * Sender, const System :: AnsiString Path, const System :: AnsiString Str)
{
    ShowMessage(Str); 
}
// ------------------------------------------------ ---------------------------
void __fastcall TForm1 :: Button1Click (TObject * Sender)
{
    TALXMLDocument * aXMLDocument = new TALXMLDocument ("root");
    aXMLDocument-> OnParseText = OnParseText;
}

I get an error:  

[bcc32 Error] Unit1.cpp (30): E2235 Member function must be called or its address taken

How to declare an event?

Upvotes: 1

Views: 209

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 596537

In C++, array properties can't be overloaded solely on their index type. So you will have to rename one of the offending properties, there is no other option. And then I would suggest you file a report with the library author asking to make the library be more friendly to C++ users.


In the Delphi code, TAlXMLParseTextEvent is a reference to an anonymous method:

TAlXMLParseTextEvent = reference to procedure (Sender: TObject; const Path, Str: AnsiString);

Which is why it gets translated to an __interface on the C++ side (because anonymous methods really are implemented behind the scenes using interfaces). Delphi-style anonymous methods require extra handling in C++, as is documented on Embarcadero's DocWiki:

How to Handle Delphi Anonymous Methods in C++

Under the cover, Delphi implements anonymous methods types (also known as method references) via an interface that implements an Invoke(...) method.

So a method that takes a method reference parameter in Delphi is exposed to C++ as a method that takes an interface.

As such, your C++ code would need to do something more like this instead:

struct TParseTextMethod
{
    void operator()(TObject *Sender, const AnsiString Path, const AnsiString Str)
    {
        ShowMessage(Str); 
    }
};
//------------------------------------------------ ---------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    TALXMLDocument *aXMLDocument = new TALXMLDocument("root");

    // the TMethodRef functor is defined in the documentation,
    // you can copy/paste it as-is into your code...
    typedef TMethodRef<TAlXMLParseTextEvent,
                     TParseTextMethod,
                     void,
                     TObject *Sender,
                     const AnsiString,
                     const AnsiString> MyMethRef;

    aXMLDocument->OnParseText = _di_TAlXMLParseTextEvent(new MyMethRef(TParseTextMethod()));
}

Or, you can streamline the functor usage a bit more by not using the TMethodRef wrapper (see Inheritance and Interfaces and Implementing Interfaces):

class TParseTextMethod : public TCppInterfacedObject<TAlXMLParseTextEvent>
{
public:
    TParseTextMethod() {}
    INTFOBJECT_IMPL_IUNKNOWN(TInterfacedObject);

    void __fastcall Invoke(TObject *Sender, const AnsiString Path, const AnsiString Str)
    {
        ShowMessage(Str); 
    }
};
//------------------------------------------------ ---------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    TALXMLDocument *aXMLDocument = new TALXMLDocument("root");

    aXMLDocument->OnParseText = _di_TAlXMLParseTextEvent(new TParseTextMethod());
}

Or, if you want to keep using your OnParseText() method as-is, then you will have to wrap it inside a functor, eg:

void __fastcall TForm1::OnParseText(TObject *Sender, const AnsiString Path, const AnsiString Str)
{
    ShowMessage(Str); 
}
//------------------------------------------------ ---------------------------
typedef void __fastcall (__closure *TAlXMLParseTextMethod)(TObject *Sender, const AnsiString Path, const AnsiString Str);

struct TParseTextMethod
{
    TAlXMLParseTextMethod Method;

    TParseTextMethod(TAlXMLParseTextMethod aMethod) : Method(aMethod) {}

    void operator()(TObject *Sender, const AnsiString Path, const AnsiString Str)
    {
        Method(Sender, Path, Str);
    }
};
//------------------------------------------------ ---------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    TALXMLDocument *aXMLDocument = new TALXMLDocument("root");

    typedef TMethodRef<TAlXMLParseTextEvent,
                     TParseTextMethod,
                     void,
                     TObject *Sender,
                     const AnsiString,
                     const AnsiString> MyMethRef;

    aXMLDocument->OnParseText = _di_TAlXMLParseTextEvent(new MyMethRef(TParseTextMethod(&OnParseText)));
}

Or:

void __fastcall TForm1::OnParseText(TObject *Sender, const AnsiString Path, const AnsiString Str)
{
    ShowMessage(Str); 
}
//------------------------------------------------ ---------------------------
typedef void __fastcall (__closure *TAlXMLParseTextMethod)(TObject *Sender, const AnsiString Path, const AnsiString Str);

class TParseTextMethod : public TCppInterfacedObject<TAlXMLParseTextEvent>
{
public:
    TAlXMLParseTextMethod Method;

    TParseTextMethod(TAlXMLParseTextMethod aMethod) : Method(aMethod) {}
    INTFOBJECT_IMPL_IUNKNOWN(TInterfacedObject);

    void __fastcall Invoke(TObject *Sender, const AnsiString Path, const AnsiString Str)
    {
        Method(Sender, Path, Str);
    }
};
//------------------------------------------------ ---------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    TALXMLDocument *aXMLDocument = new TALXMLDocument("root");

    aXMLDocument->OnParseText = _di_TAlXMLParseTextEvent(new TParseTextMethod(&OnParseText));
}

Alternatively, if you are using one of the Clang-based compilers then you can use a C++ style lambda instead of a functor:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    TALXMLDocument *aXMLDocument = new TALXMLDocument("root");
    aXMLDocument->OnParseText = [](TObject*, const AnsiString, const AnsiString Str) {
        ShowMessage(Str);
    };
}

Or:

void __fastcall TForm1::OnParseText(TObject *Sender, const AnsiString Path, const AnsiString Str)
{
    ShowMessage(Str); 
}
//------------------------------------------------ ---------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    TALXMLDocument *aXMLDocument = new TALXMLDocument("root");
    aXMLDocument->OnParseText = [this](TObject *Sender, const AnsiString Path, const AnsiString Str) {
        OnParseText(Sender, Path, Str);
    };
}

Upvotes: 1

Related Questions