0

Based on the credited answer to How can I propagate exceptions between threads?, I created a little test application to explore propagation:

#include <vcl.h>
#pragma hdrstop

#include<iostream>
#include<thread>
#include<exception>
#include<stdexcept>
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

static std::exception_ptr teptr = nullptr;
static UnicodeString status;

void __fastcall f()
{
    try
    {
        throw std::runtime_error("To be passed between threads");
    } catch(...) {
        teptr = std::current_exception();
        if (!teptr) {
            status += U"; NULL teptr";
        } else {
            status += U"; Non-NULL teptr";
        }
    }
}

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    std::thread mythread(f);
    mythread.join();

    std::this_thread::sleep_for(std::chrono::seconds(3));

    if (teptr) {
        status += U"; Button1Click non-NULL teptr";
        try
        {
            std::rethrow_exception(teptr);
        } catch(const std::exception &ex) {
            std::cerr << L"Thread exited with exception: " << ex.what() << "\n";
        }
    } else {
        status += U"; Button1Click NULL teptr";
    }
    Application->MessageBox(status.c_str(), L"Button1Click", MB_OK);
}

The results in the message box turn out to be "; NULL teptr; Button1Click NULL teptr", so to me, it looks like std::current_exception() failed to work. How can I get an application-thrown error to propagate back to the program that launched the thread?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Vic Fanberg
  • 983
  • 1
  • 7
  • 14
  • `std::current_exception()` was [broken in XE8](https://quality.embarcadero.com/browse/RSP-11426), fixed in 10.1, then [broke again in 10.3](https://quality.embarcadero.com/browse/RSP-24280). On a side note, you don't need `sleep_for()` since `join()` blocks the calling thread until the worker thread is finished running. – Remy Lebeau May 04 '20 at 20:53
  • Thanks, Remy. You are always so helpful. I guess it is back to considering 10.2 as the last stable release :-( – Vic Fanberg May 05 '20 at 10:52
  • or, you could simply not propagate exceptions across thread boundaries. In practice, it is not very useful. In 20+ years of coding in C++, I've rarely needed to do that. It is generally sufficient to propagate the fact that an exception occurred rather than propagate the exception itself. – Remy Lebeau May 05 '20 at 16:07
  • That is an interesting idea. My concern was that I've offloaded extracting data from a database to a thread and wanted to be able to throw exceptions back to the user about such things as network problems. – Vic Fanberg May 05 '20 at 18:34
  • VCL exceptions (derived from `System::Exception`) can be propagated across threads (see [`System::AcquireExceptionObject()`](http://docwiki.embarcadero.com/Libraries/en/System.AcquireExceptionObject), which is what `TThread` uses internally to set its `FatalException` property if an uncaught exception escapes `Execute()`) and then re-thrown. But as you discovered, acquiring C++ exceptions is theoretically possible, but currently broken. But you can capture the exception's class name and error message, and then throw a new exception using that info, albeit you will probably lose some details. – Remy Lebeau May 05 '20 at 19:01

0 Answers0