1

Edit: Now that I have a better idea of what is going on, I think I can better phrase this question so it is more useful.

I am trying to replicate the following delphi code in C++

TThread.Queue(nil, 
    procedure
    begin
        LogMessage("test");
    end
    );

The purpose of the code is to call a method which updates a TMemo on a form in a thread safe manner. Here is the C++ version of the method I am trying to call with Thread.Queue

void __fastcall TClientForm::LogMessage( String message )
{
    MemoLog->Lines->Add( message );
}

Because I am using a BCC32 compiler without CLANG enhancements, using a Lambda is not an option. Instead according to this documentation I need to create a class which inherits from TThreadProcedure which overrides the Invoke() method to do the work I need done. Then I can pass an instance of that class into TThread::Queue.

I created the following class which inherits TThreadProcuedure and contains an invoke method.

class TMyThreadProcedure : TThreadProcedure
{
    void __fastcall Invoke( String message );
};

However, since TThreadProcedure is an abstract class, I cannot simply create an instance of it to pass into TThread::Queue. What is the proper way to inherit from TThreadProcedure and define a function to be called when I pass an instance of my class into TThread::Queue?

James Hogle
  • 3,010
  • 3
  • 22
  • 46
  • It's in the docs: http://docwiki.embarcadero.com/RADStudio/Seattle/en/How_to_Handle_Delphi_Anonymous_Methods_in_C%2B%2B – David Heffernan Oct 02 '15 at 20:13
  • Thank you! Ive read through this a couple of times now, but I am still not understanding what to do here :( – James Hogle Oct 02 '15 at 20:29
  • You haven't shown any C++ code at all. You haven't shown the method you want to call. Your Delphi example's anon method seems pointless. Do you need a lambda? – David Heffernan Oct 02 '15 at 20:34
  • I have updated my answer with more details. My first thought was to use a lambda. However, I am using the BCC32 compiler which I believe does not have the enhanced CLANG features required for lambda. – James Hogle Oct 02 '15 at 20:40
  • The documentation is very clear on this. You have to either 1) derive a class that implements the appropriate interface (in this case, `TThreadProcedure`) overriding the `Invoke()` method to do your desired work, and then you can pass an instance of that class to `TThread::Queue()`, or 2) in the C++11 compilers only, you can use a lambda in place of an anonymous procedure. Either way, the documentation shows you exactly what to do, so what is the problem exactly? – Remy Lebeau Oct 02 '15 at 20:41
  • `_di_TThreadProcedure` is a `DelphiInterface` smart wrapper, `TThreadProcedure` is the actual interface. – Remy Lebeau Oct 02 '15 at 20:44
  • @RemyLebeau I have created a class which derives from TThreadProcedure, but since it is an abstract class, how do I create an instance of it to pass into `TThread::Queue()`? – James Hogle Oct 02 '15 at 20:56
  • @JamesHogle: As I said, `TThreadProcedure` has an `Invoke()` method that your class must implement. Did you look at the documentation? It has an example. – Remy Lebeau Oct 02 '15 at 20:59
  • @RemyLebeau I did, I am still very new to this language so I am having a hard time piecing it all together. – James Hogle Oct 02 '15 at 21:06

1 Answers1

7

As stated in the documentation:

How to Handle Delphi Anonymous Methods in C++

You have two choices:

  1. derive a class 1 that implements the appropriate interface (in this case, Classes::TThreadProcedure), overriding the Invoke() method to do your desired work. You can then pass an instance of that class to TThread::Queue(). For example:

    class TLogMessageRef : public TCppInterfacedObject<Classes::TThreadProcedure>
    {
    private:
        TClientForm *form;
    public:
        TLogMessageRef(TClientForm* _form) : form(_form) {}
        INTFOBJECT_IMPL_IUNKNOWN(TInterfacedObject);
    
        void __fastcall Invoke()
        {
            form->LogMessage("test");
        }
    };
    
    TThread::Queue(NULL,
        Classes::_di_TThreadProcedure(
            new TLogMessageRef(this)
        )
    );
    

    (1 the use of TCppInterfacedObject and INTFOBJECT_IMPL_IUNKNOWN is covered elsewhere in the documentation: Inheritance and Interfaces)

    The documentation also provides a reusable TMethodRef class to help with the implementation, if you need to use anonymous methods/procedures in multiple places of your code:

    Using a Functor (Function Object)

    For example:

    struct LogMsg
    {
        TClientForm* form;
        LogMsg(TClientForm *_form) : form(_form) {}
        void operator()()
        {
            form->LogMessage("test");
        }
    };
    
    typedef TMethodRef<Classes::TThreadProcedure, LogMsg, void> MyMethRef;
    
    TThread::Queue(NULL,
        Classes::_di_TThreadProcedure(
            new MyMethRef(
                LogMsg(this)
            )
        )
    );
    
  2. in the Clang-based C++11 compilers only, you can use a C++ lambda anywhere an anonymous procedure/method is expected:

    TThread::Queue(NULL, 
        [this]() -> void { LogMessage("test"); }
    );
    
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Could you explain how this overrides the `Invoke()` method? Is the `struct LogMsg` an example of a Functor, like is described in this question? http://stackoverflow.com/questions/356950/c-functors-and-their-uses – James Hogle Oct 02 '15 at 21:13
  • Look at the `TMethodRef` class provided in the [documentation](http://docwiki.embarcadero.com/RADStudio/XE8/en/How_to_Handle_Delphi_Anonymous_Methods_in_C%2B%2B). It derives from the specified interface and overrides the `Invoke()` method to call the specified functor. And yes, in this case, `LogMsg` is a functor (hence its `operator()`). – Remy Lebeau Oct 02 '15 at 22:50