I have this template class:
template <typename ResultType>
class TaskResult: public BArchivable {
public:
TaskResult(const BMessage &archive)
{
// archive.PrintToStream();
void *data = nullptr;
ssize_t size = 0;
type_code type;
if (archive.GetInfo(fResultFieldName, &type) == B_OK) {
status_t error = archive.FindData(fResultFieldName, type,
(const void**)&data, &size);
if (error != B_OK)
throw runtime_error("Can't unarchive TaskResult instance");
else
fResult = *(ResultType*)(data);
} else {
throw runtime_error("Can't unarchive TaskResult instance");
}
if (archive.FindInt32(fResultTaskIdName, &fId) != B_OK) {
throw runtime_error("Can't unarchive TaskResult instance");
}
}
~TaskResult() {}
ResultType GetResult()
{
auto ptr = TaskExceptionMap[fId];
if (ptr)
rethrow_exception(ptr);
else
return fResult;
}
virtual status_t Archive(BMessage *archive, bool deep)
{
status_t result;
result = BArchivable::Archive(archive, deep);
type_code type = BMessageType<ResultType>::Get();
result = archive->AddData(fResultFieldName, type, &fResult, sizeof(fResult));
result = archive->AddInt32(fResultTaskIdName, fId);
result = archive->AddString("class", "TaskResult");
return result;
}
static BArchivable* Instantiate(BMessage* archive)
{
if (validate_instantiation(archive, "TaskResult"))
return new TaskResult<ResultType>(archive);
else
return nullptr;
}
private:
friend class Task<ResultType>;
const char* fResultFieldName = "TaskResult::Result";
const char* fResultTaskIdName = "TaskResult::ID";
ResultType fResult;
thread_id fId;
TaskResult() {}
};
template <>
class TaskResult<void>: public BArchivable {
public:
TaskResult(const BMessage &archive)
: fResult(true)
{
// archive.PrintToStream();
if (archive.FindInt32(fResultTaskIdName, &fId) != B_OK) {
throw runtime_error("Can't unarchive TaskResult instance");
}
}
~TaskResult() {}
void GetResult()
{
auto ptr = TaskExceptionMap[fId];
if (ptr)
rethrow_exception(ptr);
}
virtual status_t Archive(BMessage *archive, bool deep) const
{
status_t result;
result = BArchivable::Archive(archive, deep);
result = archive->AddInt32(fResultTaskIdName, fId);
result = archive->AddString("class", "TaskResult");
return result;
}
static BArchivable* Instantiate(BMessage* archive)
{
if (validate_instantiation(archive, "TaskResult"))
return new TaskResult<void>(archive);
else
return nullptr;
}
private:
friend class Task<void>;
const char* fResultFieldName = "TaskResult::Result";
const char* fResultTaskIdName = "TaskResult::ID";
bool fResult;
thread_id fId;
TaskResult() {}
};
It fails to compile with this error:
In file included from src/helpers/Task.cpp:7:
src/helpers/Task.h: In static member function 'static BArchivable* Genio::Task::TaskResult<void>::Instantiate(BMessage*)':
src/helpers/Task.h:252:68: error: 'BMessage::BMessage(BMessage*)' is private within this context
252 | return new TaskResult<void>(archive);
| ^
In file included from /boot/system/develop/headers/os/support/Archivable.h:10,
from /boot/system/develop/headers/os/app/Handler.h:12,
from /boot/system/develop/headers/os/app/Looper.h:13,
from /boot/system/develop/headers/os/interface/Window.h:9,
from /boot/system/develop/headers/os/interface/Alert.h:11,
from src/helpers/Utils.h:13,
from src/helpers/Task.h:13:
/boot/system/develop/headers/os/app/Message.h:560:65: note: declared private here
560 | BMessage(BMessage* message);
| ^~~~~~~~
I can’t figure out why return new TaskResult<void>(archive)
yields the error above.
Does anybody know why?
NOTE:
Just a bit of context here, TaskResult<> is part of a library I’m writing as part of the Genio framework.
It is a wrapper for the result of an execution unit invoked by the class:
template <typename ResultType>
class Task {
public:
using native_handle_type = thread_id;
template<typename Name, typename Messenger, typename Function, typename... Args>
Task(Name name, Messenger messenger, Function&& function, Args&&... args)
{}
...
}
It works like std::thread and is insipred by the thread functions and classes in libwalter but it uses modern C++ features like template packs (aka template variadic args), TAD, SFINAE, etc.
It accepts any callable with perferct argument forwarding and is declared like this:
Genio::Task::Task<status_t> task("test", new BMessenger(this),
&Genio::Git::GitRepository::Clone,
fURL->Text(),
fPathBox->Path());
task.Run();
at the end of the execution it sends a BMessage back to the caller by archiving a TaskResult instance.
case TASK_RESULT_MESSAGE:
{
try {
// auto result = TaskResult<status_t>(*msg).GetResult();
// OKAlert("", BString("MessageReceived() Result: ") << result, B_INFO_ALERT);
TaskResult<void>(*msg).GetResult();
OKAlert("", BString("MessageReceived() Result: terminated"), B_INFO_ALERT);
} catch(std::exception &ex) {
OKAlert("", BString("MessageReceived() Exception: ") << ex.what(), B_INFO_ALERT);
}
}
TaskResult also wraps any uncatched exception by transporting it to the caller via exception_ptr, current_exception() and rethrow_exception().
For the records, I have abandoned the template specialization route in favor of a true polimorfic approach using std::any. Still, I’m wondering why this old version does not compile.