Упрощение шаблона C ++, который включает имя переменной и вывод функции - PullRequest
1 голос
/ 14 января 2020

У меня в коде слишком много раз:

class c_outcome {
    public:
        bool success;
        std::string error;
};

c_outcome out;
out = do_sth(a, input, "a");
if (!out.success) { return do_sth2(out.error); };
out = do_sth(b, input, "b");
if (!out.success) { return do_sth2(out.error); };
out = do_sth(c, input, "c");
if (!out.success) { return do_sth2(out.error); };

Как мне сократить его, избегая повторений? В принципе, я бы хотел:

Я не знаю, возможно ли это / удобно, но не выглядит правильным повторять шаблон непрерывно.

Ответы [ 3 ]

4 голосов
/ 14 января 2020

Не прибегая к уродливым макросам, вы можете сократить обработку ошибок в красивом функциональном стиле. Взгляните:

class c_outcome {
public:
    bool success;
    std::string error;

    template<class Fun, class...Args>
    c_outcome then(Fun&& fun, Args&&... args) const {
        if (success) {
            return std::forward<Fun>(fun)(std::forward<Args>(args)...);
        } else {
            return *this;
        }
    }

    template<class ErrorHandler>
    c_outcome except(ErrorHandler&& fun) const {
        if (!success) {
            std::forward<ErrorHandler>(fun)(error);
        }
        return *this;
    }
};

template<class Fun, class...Args>
c_outcome run(Fun&& fun, Args&&... args) {
    return std::forward<Fun>(fun)(std::forward<Args>(args)...);
}

С таким кодом вы можете использовать его следующим образом:

c_outcome do_everything() {
    return run(do_sth, a, input, "a")
        .then(do_sth, b, input, "b")
        .then(do_sth, c, input, "c")
        .except(do_sth2);
}

Основным преимуществом этого кода является универсальный c. Он будет обрабатывать почти все типы типов, которые вы можете себе представить.

РЕДАКТИРОВАТЬ: я обновил код, так что теперь его можно использовать как-то рекурсивно для обработки ошибок

1 голос
/ 14 января 2020

Я не думаю, что есть хороший способ реализовать do_everything(), потому что вам нужны строки с именами переменных. Вместо этого вам нужно написать каждую переменную отдельно.

Один из вариантов - сначала собрать имена переменных.

// You can also use a macro to simplify this 
const std::vector<std::pair<std::reference_wrapper<type>, std::string>> lst = {
    {a, "a"}, {b, "b"}, {c, "c"}
};
for (const auto& pair : lst) {
    auto out = do_sth(pair.first, input, pair.second.c_str());
    if (!out.success) { return do_sth2(out.error); };
}

Другой вариант - использовать лямбду для выполнения общих частей.

auto wrapper = [input](const type& p, const char* str) {
    auto out = do_sth(p, input, str);
    if (!out.success) { return do_sth2(out.error); };
};

#define CALL(p) wrapper(p, #p)

CALL(a);
CALL(b);
CALL(c);
0 голосов
/ 14 января 2020

Если вы можете использовать повышение, есть BOOST_PP_SEQ_FOR_EACH. В вашем примере это должно выглядеть так:

#define DO_THING_ONCE(_, input, a) \
    out = do_sth(a, input, BOOST_PP_STRINGIZE(a)); \
    if (!out.success) { return do_sth2(out.error); }

#define DO_EVERYTHING(input, ...) \
    c_outcome out; \
    BOOST_PP_SEQ_FOR_EACH(DO_THING_ONCE, input, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

По существу, 2-й аргумент BOOST_PP_SEQ_FOR_EACH передается на каждую итерацию, а 3-й аргумент - это последовательность, которая повторяется, поэтому вам нужно только преобразовать переменную c аргументы, чтобы увеличить pp-последовательность и написать подходящий макрос.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...