Как условно передать перенаправленные аргументы в std::invoke (если это определенный класс), полученные из шаблона std::C++

Программы на C++. Форум разработчиков
Ответить Пред. темаСлед. тема
Anonymous
 Как условно передать перенаправленные аргументы в std::invoke (если это определенный класс), полученные из шаблона std::

Сообщение Anonymous »

Я создал функцию вызова члена класса, которая выполняет отложенный вызов, например:

Код: Выделить всё

func = NotifyAll(
&get_file_listener,
&GetFileListener::OnGetFileSuccess, container.c_str(), name.c_str());
// ... long delay and different scope
func();
Но из-за характера «выполнения, происходящего позже», я понимаю, что переменные, захватываемые по значению, будут работать, но, очевидно, это не будет работать для аргументов char*, поскольку в:

Код: Выделить всё

class ListenerInterface {
public:
virtual ~ListenerInterface() {}
};

class GetFileListener: public ListenerInterface {
public:
virtual void OnGetFileSuccess(const char* container, const char* name);
virtual ~GetFileListener();
};
Я сделал это, создав шаблон и перенаправив функцию-член и тип класса, а затем вызвав функцию, когда это необходимо (создав уведомление с помощью NotifyAll и вызвав возвращающую лямбду). вызывает NotifyAllNow):

Код: Выделить всё

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)...);
}
Мне как-то нужно продлить срок службы некоторых параметров, думаю, что-то вроде дополнительного LifeExtender (или что-то еще) может сработать:

Код: Выделить всё

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::conditional_t или сделать отдельную специализацию, используя std::enable_if для приведения к нему char*, когда аргумент имеет тип LifeExtender, а если нет, просто передайте его.
Думаю, мне следует почерпнуть вдохновение из этого поста: Использование 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]
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «C++»