0 / 0 / 0
Регистрация: 28.02.2023
Сообщений: 26
1

Оболочка для возвращаемого значения функции и std::optional

03.03.2023, 14:02. Показов 1883. Ответов 20

Author24 — интернет-сервис помощи студентам
Добрый день!
Есть вот такой код:
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
template <typename Func, typename Res = typename boost::function_types::result_type<Func>::type>
struct TaskWrapped
    {
        const std::function<Func> taskUnwrapped_;
        
        explicit TaskWrapped(std::function<Func> &taskUnwrapped) 
            : taskUnwrapped_(taskUnwrapped)
        {}
        explicit TaskWrapped(std::function<Func> &&taskUnwrapped) 
            : taskUnwrapped_(std::move(taskUnwrapped))
        {}
 
        template <typename... Args>
        std::optional<Res> operator()(Args &&...args) const noexcept
        //ошибка если Res = void
        {
            try
            {
                boost::this_thread::interruption_point();
            }
            catch (const boost::thread_interrupted &)
            {
                std::cerr << "thread interruption" << std::endl;
            }
            try
            {
                return taskUnwrapped_(std::forward<Args>(args)...);
            }
            catch (const std::exception &ex)
            {
                std::cerr << ex.what() << std::endl;
            }
            catch (const boost::thread_interrupted &)
            {
                std::cerr << "thread interruption" << std::endl;
            }
            catch (...)
            {
                std::cerr << "unknown error when calling the function: "
                          << boost::typeindex::type_id<Func>() << std::endl;
            }
            return std::nullopt;
        }
    };


Нужно сделать оболочку для возвращаемого значения функции, даже если оно void. Также нужно уведомить пользователя о том, что было исключение, для этого используется std::option.
Пока что так:
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
namespace detail
    {
        template <typename Ty, typename = std::enable_if_t<std::is_same_v<Ty, void>>>
        struct non_void_return_value
        {
            using type = std::void_t<Ty>;//?????
        };
 
        template <typename Ty>
        struct non_void_return_value<Ty>
        {
            using type = Ty;
        };
 
        template <typename Ty>
        using non_void_return_value_t = typename non_void_return_value<Ty>::type;
 
        template <typename Ty>
        struct ProxyResult
        {
            using type = non_void_return_value_t<Ty>;
            std::optional<type> value_;
 
            ProxyResult(type&& value) : value_(value) {}
            ProxyResult(std::nullopt_t opt) : value_(opt) {}
            ProxyResult(void) {}
        };
    
        void foo(){}
        
        ProxyResult<void> foo2()
        {
            return foo();//error
        }
    }
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
03.03.2023, 14:02
Ответы с готовыми решениями:

Ключевое слово const для возвращаемого значения из функции
Думал что это означает следующее &quot;Нельзя изменить значение которое вернет функция&quot; Но проверил и...

Самый простой PHP код для вызова хранимой функции PostgreSQL и вывода возвращаемого значения?
Имя хранимой в PostgreSQL функции: check_login_and_password Аргументы: &quot;input_login&quot; character...

std::optional<T> при std::is_destructible_v<T> == false
Всем привет! Исследую несколько разных реализаций std::optional, и наткнулся на интересную вещь:...

Тип возвращаемого значения функции
есть класс point. в другом классе есть член функция point GetUpperLeft() { return itsUpperLeft; }...

Вывод возвращаемого значения функции
Есть функция: CREATE OR REPLACE FUNCTION block_rows_client(clID IN NUMBER, fname IN VARCHAR2)...

20
6340 / 3511 / 1427
Регистрация: 07.02.2019
Сообщений: 8,977
03.03.2023, 17:49 2
Лучший ответ Сообщение было отмечено NanoCase как решение

Решение

Цитата Сообщение от NanoCase Посмотреть сообщение
Нужно сделать оболочку для возвращаемого значения функции, даже если оно void.
Что-то в этом духе
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
template<class Res>
struct my_result {
    using type = std::optional<Res>;
};
 
struct dummy {};
 
template<>
struct my_result<void> {
    using type = std::optional<dummy>;
};
 
//...
 
template <typename... Args>
typename my_result<Res>::type operator()(Args &&...args) const noexcept
{
    //...
    try
    {
        if constexpr (std::is_void_v<Res>) {
            taskUnwrapped_(std::forward<Args>(args)...);
            return dummy{};
        } else {
            return taskUnwrapped_(std::forward<Args>(args)...);
        }
    }
    //...
    return std::nullopt;
}
1
0 / 0 / 0
Регистрация: 28.02.2023
Сообщений: 26
03.03.2023, 22:02  [ТС] 3
Хорошее решение. Но в языке следующий код является валидным:
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
void foo()
{
    std::cout << "foo" << std::endl;
}
void foo2()
{
    return foo();
}
int main()
{
    foo2();
}

Думаю что можно как-то сыграть от этого при помощи SFINAE и приведений типов.
0
6340 / 3511 / 1427
Регистрация: 07.02.2019
Сообщений: 8,977
03.03.2023, 22:37 4
Цитата Сообщение от NanoCase Посмотреть сообщение
Думаю что можно как-то сыграть от этого при помощи SFINAE и приведений типов.
Выражением prvalue типа void нельзя инициализировать объект. Так что либо if constexpr, либо то же самое, только через специализацию какого-нибудь "инвокера".
1
0 / 0 / 0
Регистрация: 28.02.2023
Сообщений: 26
03.03.2023, 22:52  [ТС] 5
Понял. Спасибо за ответ!
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
08.03.2023, 21:51 6
Лучший ответ Сообщение было отмечено zayats80888 как решение

Решение

Цитата Сообщение от NanoCase Посмотреть сообщение
Думаю что можно как-то сыграть от этого
Можно сыграть.
Вот без специализаций и условий.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace detail {
 
struct Dummy {};
 
template <typename Res>
std::optional<Res> operator,(Res&& res, std::optional<Dummy>) {
    return std::forward<Res>(res);
}
 
} // detail
 
template <typename... Args>
auto operator()(Args&& ...args)
{
    using detail::Dummy;
    using Ret = decltype(taskUnwrapped_(std::forward<Args>(args)...), std::optional<Dummy>());
    
    try {
        return taskUnwrapped_(std::forward<Args>(args)...), std::optional<Dummy>();
    }
    //....
    return Ret();
}
2
0 / 0 / 0
Регистрация: 28.02.2023
Сообщений: 26
09.03.2023, 23:54  [ТС] 7
DrOffset, т.е. перегружая оператор запятая у нас появляется возможность вывести возвращаемое значение функции в виде шаблона и если на 7 строчке провал подстановки то мы инициализируем Ret пустышкой?
0
6340 / 3511 / 1427
Регистрация: 07.02.2019
Сообщений: 8,977
10.03.2023, 00:52 8
Цитата Сообщение от NanoCase Посмотреть сообщение
и если на 7 строчке провал подстановки то мы инициализируем Ret пустышкой?
Ага, только не на 7, а на 6 (точнее, на этапе формирования списка кандидатов в перегрузи).
И на 19 строчке в конструктор optional нужно передать объект Dummy, иначе результат неотличим от того, что в строке 22.
2
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
10.03.2023, 06:53 9
Цитата Сообщение от zayats80888
Dummy, иначе результат неотличим от того, что в строке 22.
Да, согласен, спасибо, этот момент потерялся.
1
0 / 0 / 0
Регистрация: 28.02.2023
Сообщений: 26
11.03.2023, 22:14  [ТС] 10
zayats80888 DrOffset, продолжаю улучшать оболочку, хочу чтобы функцию можно было вызывать из объекта, т.е. сохраняем в классе указатель на вызывающую сторону, когда хотим вызвать проверяем указатель на nullptr и вызываем соответствующий вариант функции. Но такой подход походу не работает, компилятор выдает ошибку. Можете направить меня в нужную сторону, пожалуйста

Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
struct dummy
    {
    };
 
    template <typename Res>
    std::optional<Res> operator,(Res &&res, std::optional<dummy>)
    {
        return std::forward<Res>(res);
    }
 
template <class Func, class Obj = detail::dummy*>
    struct task_wrapped
    {
        const std::function<Func> taskUnwrapped_;
        const Obj* caller_;
        
        explicit task_wrapped(std::function<Func> &taskUnwrapped, Obj* caller = nullptr) 
           : taskUnwrapped_(taskUnwrapped), caller_(caller)
        {
        }
        explicit task_wrapped(std::function<Func> &&taskUnwrapped) 
           : taskUnwrapped_(std::move(taskUnwrapped))
        {
        }
 
        template <typename... Args>
        auto operator()(Args &&...args) const noexcept
        {
            using detail::dummy;
            if constexpr(caller_)
                using Ret = decltype(caller_->taskUnwrapped_(std::forward<Args>(args)...), 
                                                                                            std::optional<dummy>());
            else 
                using Ret = decltype(taskUnwrapped_(std::forward<Args>(args)...), 
                                                                                     std::optional<dummy>());
            
            //.....
            try
            {
                if constexpr(caller_ != nullptr) 
                        return caller_->taskUnwrapped_(std::forward<Args>(args)...), 
                                                             std::optional<detail::dummy>(detail::dummy{});
                else return taskUnwrapped_(std::forward<Args>(args)...), 
                                                                          std::optional<dummy>(dummy{});
            }
           //.....
            return Ret();
        }
    };
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
11.03.2023, 22:16 11
NanoCase, у вас же есть std::function, нет нужды вводить разграничение на уровне вызова, когда можно его спрятать на уровне создания функтора.
1
0 / 0 / 0
Регистрация: 28.02.2023
Сообщений: 26
11.03.2023, 22:32  [ТС] 12
DrOffset, в std::function можно указывать вызывающую сторону?
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
11.03.2023, 22:38 13
Цитата Сообщение от NanoCase Посмотреть сообщение
в std::function можно указывать вызывающую сторону?
В std::bind или в лямбде можно.

Вот вариант:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <iostream>
#include <optional>
#include <string>
#include <functional>
 
namespace detail {
    struct dummy
    {
    };
 
    template <typename Res>
    std::optional<Res> operator,(Res &&res, std::optional<dummy>)
    {
        return std::forward<Res>(res);
    }
    
    template <typename F>
    struct remove_function_top_const;
    template <typename R, typename ...Args>
    struct remove_function_top_const<R(Args...)> {
        using type = R(Args...);
    };
    template <typename R, typename ...Args>
    struct remove_function_top_const<R(Args...) const> {
        using type = R(Args...);
    };
}
    template <class Func>
    struct task_wrapped
    {
        const std::function<typename detail::remove_function_top_const<Func>::type> taskUnwrapped_;
 
        template <typename Obj>
        explicit task_wrapped(Func (Obj::*fptr), Obj* caller) 
            : taskUnwrapped_(std::bind(fptr, caller))
        { }
        explicit task_wrapped(std::function<Func> &&taskUnwrapped) 
            : taskUnwrapped_(std::move(taskUnwrapped))
        {
        }
 
        template <typename... Args>
        auto operator()(Args &&...args) const noexcept
        {
            using detail::dummy;
            using Ret = decltype(taskUnwrapped_(std::forward<Args>(args)...), std::optional<dummy>());
            
            //.....
            try
            {
                return taskUnwrapped_(std::forward<Args>(args)...), std::optional<dummy>(dummy{});
            }
            catch(...)
            {
            }
            return Ret();
        }
    };
 
int main()
{
    std::string a = "test";
    
    task_wrapped<char const*() const> x(&std::string::c_str, &a);
    
    std::cout << x().value();
}
https://wandbox.org/permlink/CJc6NIesab4t2Abr
1
0 / 0 / 0
Регистрация: 28.02.2023
Сообщений: 26
12.03.2023, 00:44  [ТС] 14
DrOffset, Вроде разобрался. Спасибо!

Добавлено через 1 час 25 минут
DrOffset, проблемка вылезла, std::bind нужны placeholders на все аргументы вызываемой функции, вроде как решается заменой bind на аналогичную лямбду, с захватом функции и caller. Вот решение близкое к правде, но он ругается на сам вызов функции в лямбде

Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <iostream>
#include <optional>
#include <string>
#include <functional>
 
namespace detail {
    struct dummy
    {
    };
 
    template <typename Res>
    std::optional<Res> operator,(Res &&res, std::optional<dummy>)
    {
        return std::forward<Res>(res);
    }
    
    template <typename F>
    struct remove_function_top_const;
    template <typename R, typename ...Args>
    struct remove_function_top_const<R(Args...)> {
        using type = R(Args...);
    };
    template <typename R, typename ...Args>
    struct remove_function_top_const<R(Args...) const> {
        using type = R(Args...);
    };
}
    template <class Func>    
    struct task_wrapped;
 
    template <class R, class ...Args>
    struct task_wrapped<R(Args...)>
    {
        using Func = R(Args...);
        const std::function<typename detail::remove_function_top_const<Func>::type> taskUnwrapped_;
 
        template <typename Obj>
        explicit task_wrapped(Func (Obj::*fptr), Obj* caller) 
            : taskUnwrapped_([fptr, caller](Args &&...args){return caller->fptr(args...);})//error
        { }
        explicit task_wrapped(Func &taskUnwrapped) 
            : taskUnwrapped_(taskUnwrapped)
        {
        }
        
        auto operator()(Args &&...args) const noexcept
        {
            using detail::dummy;
            using Ret = decltype(taskUnwrapped_(std::forward<Args>(args)...), std::optional<dummy>());
            
            //.....
            try
            {
                return taskUnwrapped_(std::forward<Args>(args)...), std::optional<dummy>(dummy{});
            }
            catch(...)
            {
            }
            return Ret();
        }
    };
 
    typedef task_wrapped<int(int&, int&)> callback;
    struct str
    {   
        int sum(int& a, int& b)
        {
            return a + b;
        }
    };
 
    int sum(int& a, int& b)
{
    return a+b;
}
 
int main()
{
#ifdef i
    std::string a = "test";
    task_wrapped<char const*() const> x(&std::string::c_str, &a);
    std::cout << x().value();
#else
    str st;
    int a = 3, b = 6;
    task_wrapped<int(int&, int&)> task(&str::sum, &st);
    task(a, b);
    std::cout << a;
#endif
}
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
12.03.2023, 00:55 15
NanoCase, синтаксис вызова через указатель на метод другой. Плюс забыли forward
C++
1
taskUnwrapped_([fptr, caller](Args &&...args){return (caller->*fptr)(std::forward<Args>(args)...);})
1
0 / 0 / 0
Регистрация: 28.02.2023
Сообщений: 26
12.03.2023, 02:46  [ТС] 16
DrOffset, Заработало, спасибо

Добавлено через 1 час 8 минут
DrOffset, у меня ещё вопрос созрел(возможно глупый). Допустим я хочу передать в качестве параметра функции shared_ptr, который должен скопироваться в оператор() и следовательно в пользовательскую функцию. Можно ли это решить без перегрузок оператора() и тотального переписывания кода?
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
12.03.2023, 12:40 17
Цитата Сообщение от NanoCase Посмотреть сообщение
у меня ещё вопрос созрел(возможно глупый). Допустим я хочу передать в качестве параметра функции shared_ptr, который должен скопироваться в оператор() и следовательно в пользовательскую функцию. Можно ли это решить без перегрузок оператора() и тотального переписывания кода?
А в чем проблема?
C++
1
2
3
4
5
6
7
8
9
10
11
void test(std::shared_ptr<int> p) {
    // ...
}
 
int main()
{
    std::shared_ptr<int> p( new int() );
    task_wrapped<void(std::shared_ptr<int>)> x(&test);
    
    x(p);
}
0
0 / 0 / 0
Регистрация: 28.02.2023
Сообщений: 26
12.03.2023, 13:49  [ТС] 18
DrOffset, gcc 12.2 ошибку выдает на ваш код:

C++
1
2
error: no match for call to '(task_wrapped<void(std::shared_ptr<int>)>) (std::shared_ptr<int>&)'
error: cannot bind rvalue reference of type 'std::shared_ptr<int>&&' to lvalue of type 'std::shared_ptr<int>'
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
12.03.2023, 14:03 19
NanoCase, так не на мой, а на ваш. Вы же сами класс изменили. У вас оператор теперь не соответствует новому подходу. Надо так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
        auto operator()(Args ...args) const noexcept
        {
            using detail::dummy;
            using Ret = decltype(taskUnwrapped_(args...), std::optional<dummy>());
            
            //.....
            try
            {
                return taskUnwrapped_(args...), std::optional<dummy>(dummy{});
            }
            catch(...)
            {
            }
            return Ret();
        }
0
0 / 0 / 0
Регистрация: 28.02.2023
Сообщений: 26
12.03.2023, 14:14  [ТС] 20
DrOffset, я думал вы не меняли описание враппера. дело в том что такая реализация будет принимать все по значению, даже если нужно чтобы оператор принял что то по ссылке. но я уже понял что перегружать по универсальной ссылке смысла мало. решил это добавлением дополнительного уровня косвенности, что то типо такого:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef task_wrapped<void()> task;
void test(std::shared_ptr<int> p) {
    // ...
}
 
int main()
{
    std::shared_ptr<int> p( new int() );
    auto layer = [p]{test(p);};
    task x(layer);
    
    x();
    
    
}
0
12.03.2023, 14:14
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
12.03.2023, 14:14
Помогаю со студенческими работами здесь

Функции (вызов и тип возвращаемого значения)
Доброй ночи! Кто может помочь мне по следующей задачей? Есть динамический массив в main(): ...

Структура в качестве возвращаемого значения функции
Добрый день! Есть вот такая программа: #include&lt;stdio.h&gt; #include&lt;stdlib.h&gt; #include&lt;string.h&gt;...

Ошибка с приведением возвращаемого из функции значения
Необходимо написать процедуру которая показывала бы процент детей поступивших на бюджетную форму...

Действие, зависящее от возвращаемого значения функции
есть функция: privilige(user, hash, event) она проверяет, есть ли у пользователя user доступ к...

Выводится адрес функции вместо возвращаемого значения
//файл Autostart.cpp int fauto(){ //автоматическая...

Тип возвращаемого значения не соответствует типу функции
double get_left (void); { double e, c; e = ex - a; c = cx - r; return max(e, c); }...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru