Код: Выделить всё
func = NotifyAll(
&get_file_listener,
&GetFileListener::OnGetFileSuccess, container.c_str(), name.c_str());
// ... long delay and different scope
func();
Код: Выделить всё
class ListenerInterface {
public:
virtual ~ListenerInterface() {}
};
class GetFileListener: public ListenerInterface {
public:
virtual void OnGetFileSuccess(const char* container, const char* name);
virtual ~GetFileListener();
};
Код: Выделить всё
template struct extract_class_from_member_function_ptr;
template
struct extract_class_from_member_function_ptr {
using type = CLASS;
};
template
struct extract_class_from_member_function_ptr {
using type = CLASS;
};
template
struct extract_class_from_member_function_ptr {
using type = CLASS;
};
template
struct extract_class_from_member_function_ptr {
using type = CLASS;
};
template
void NotifyAllNow(T* one_time_specific_listener, FuncT&& Function, ArgTypes&&... Arguments) {
using BaseT = extract_class_from_member_function_ptr::type;
BaseT* casted_listener = (BaseT*)(one_time_specific_listener);
//assert(casted_listener != nullptr);
std::invoke(std::forward(Function), casted_listener, std::forward(Arguments)...);
}
template
auto NotifyAll(ArgTypes&&... Arguments)
{
return std::bind_front([](auto&&... args) {
NotifyAllNow(std::forward(args)...);
}, std::forward(Arguments)...);
}
Код: Выделить всё
class LifeExtender {
std::optional data;
public:
LifeExtender(const char* const str) : data{} {
if (str != nullptr) {
data = std::string(str);
}
}
LifeExtender(char* str) : data{} {
if (str != nullptr) {
data = std::string(str);
}
}
LifeExtender(const std::string& str) : data{str} {}
~LifeExtender() {}
operator const char* const() const noexcept {
if (data) {
return data->c_str();
}
return nullptr;
}
operator char*() const noexcept {
if (data) {
return (char*)data->c_str();
}
return nullptr;
}
};
Код: Выделить всё
func_fixed = NotifyAll(
get_file_listener,
&GetFileListener::OnGetFileSuccess, LifeExtender(container), LifeExtender(name));
Но теперь я застрял в попытке привести любой объект LifeExtender к char* (в данном случае путем простого вызова для них приведения типа char*) в шаблонном вызове:
Код: Выделить всё
std::invoke(std::forward(Function), casted_listener, std::forward(Arguments)...);
Думаю, мне следует почерпнуть вдохновение из этого поста: Использование std ::перейдем к приведенным аргументам
Но шаблоны все еще иногда сложны для меня, я не думаю, что знаю, как это применить.
Что было бы правильным способом решить эту проблему продления жизни каким-то аккуратным способом, чтобы я мог продолжать вызывать функции-члены позже (уведомления о проблемах)?
К сожалению, я не могу изменить параметры из методов, которые получают уведомления, поскольку они вообще не контролируются мной, к сожалению, мне приходится передавать char*.
Изменение всего на std::string сделало бы это это не проблема, но мне приходится работать с картами, которые мне сдали.
Полный пример можно найти здесь:
https://coliru.stacked-crooked.com /a/cfd98a9e6cad0f43
--------------------------
Изменить: я попробовал создать функции идентификации/пересылки (думаю, я сделал это неправильно):
Код: Выделить всё
class LifeExtender;
template< typename V>
inline typename std::enable_if::value, V&&>::type identity(V&& v)
{
return std::forward(v);
}
template< typename V>
inline typename std::enable_if::type identity(V&& v)
{
return std::forward(v);
}
template
void NotifyAllNow(T* one_time_specific_listener, FuncT&& Function, ArgTypes&&... Arguments) {
using BaseT = extract_class_from_member_function_ptr::type;
BaseT* casted_listener = (BaseT*)(one_time_specific_listener);
//assert(casted_listener != nullptr);
std::invoke(std::forward(Function), casted_listener, identity(Arguments)...);
}
Код: Выделить всё
// VVVVV ERROR VVVVV
//func_fixed = NotifyAll(
// get_file_listener,
// &GetFileListener::OnGetFileSuccess, LifeExtender(container), LifeExtender(name));
// ^^^^^ ERROR ^^^^^
// 'void NotifyAllNow(T *,FuncT &&,ArgTypes &&...)': could not deduce template argument for 'T *' from 'GetFileListener'
get_file_listener.OnGetFileSuccess(identity(LifeExtender(container)), identity(LifeExtender(name))); // fine
get_file_listener.OnGetFileSuccess(LifeExtender(container), LifeExtender(name)); // fine
func = NotifyAll(
&get_file_listener,
&GetFileListener::OnGetFileSuccess, container.c_str(), name.c_str());
}
func(); // referencing deallocated memory 'container' and 'name' here
//func_fixed(); // works as expected without UB
Редактировать2:
На данный момент я решил эту проблему следующим образом: https://coliru.stacked-crooked.com/a/6d78414211a49624
Но это не похоже на правильное решение. Я думаю, что создается слишком много копий (я согласен использовать это как есть, но мне бы хотелось иметь «правильное» решение).
На данный момент я удалил && из NotifyAll, добавил использование функции продления жизни и попытался еще немного оптимизировать это с помощью перемещения:
Код: Выделить всё
template
inline T&& extend(typename std::enable_if<
!std::same_as &&
!std::same_as, T&&>::type v)
{
return std::forward(std::move(v));
}
template
inline char*&& extend(char*&& v)
{
// Hey there,
// if you're debugging this code, and passing char* 's to a notification,
// make sure there will be no lifetime/scope issues!
// Here I'm just forwarding the char* ptr..
// If you want to use automatic life extension then consider casting it to const char*.
return std::forward(std::move(v));
}
template
inline LifeExtender extend(const char*&& v)
{
return std::forward(std::move(LifeExtender(v)));
}
template
auto NotifyAll(ArgTypes... Arguments) // unfortunately I don't know how to make this more efficient by using ArgTypes&&, it causes so much lvalue rvalue errors/headaches
{
return std::bind_front([](auto&&... args) {
NotifyAllNow(std::forward(args)...);
}, extend(std::move(Arguments))...);
}
Код: Выделить всё
template
inline typename std::enable_if::value, V&&>::type identity(V&& v)
{
return std::forward(v);
}
template
inline typename std::enable_if::type identity(V&& v)
{
return std::forward(v);
}
template
void NotifyAllNow(T* one_time_specific_listener, FuncT&& Function, ArgTypes&&... Arguments) {
using BaseT = extract_class_from_member_function_ptr::type;
BaseT* casted_listener = (BaseT*)(one_time_specific_listener);
//assert(casted_listener != nullptr);
std::invoke(std::forward(Function), casted_listener, identity(Arguments)...);
}
Код: Выделить всё
class ListenerInterface {
public:
virtual ~ListenerInterface() {}
};
struct extra {
int i;
};
class GetFileListener : public ListenerInterface {
public:
virtual void OnGetFileSuccess(const char* container, const char* name, extra* other, int i, float f, double d, char c, const extra& x, extra x2, const extra& x3, extra x4) {
std::cout
Подробнее здесь: [url]https://stackoverflow.com/questions/78411021/how-to-cast-forwarded-arguments-to-stdinvoke-conditionally-if-its-a-certain[/url]