2016-12-08 8 views
0

У меня есть код, как это:Могу ли я сделать это без макроса (в C++ 11)?

void function() 
{ 
    auto isOk=task(1); 
    if(!isOk) 
    { 
     return; 
    } 

    // more code here 

    auto isOk=task(2); 
    if(!isOk) 
    { 
     return; 
    } 

    // more code here 

    auto isOk=task(3); 
    if(!isOk) 
    { 
     return; 
    } 

    // more code here 

    auto isOk=task(4); 
    if(!isOk) 
    { 
     return; 
    } 

    // more code here 

    auto isOk=task(5); 
    if(!isOk) 
    { 
     return; 
    } 

    // more code here 

    auto isOk=task(6); 
    if(!isOk) 
    { 
     return; 
    } 

    // more code here 

    auto isOk=task(7); 
    if(!isOk) 
    { 
     return; 
    } 

    // more code here 

    auto isOk=task(8); 
    if(!isOk) 
    { 
     return; 
    } 

    // more code here 

    auto isOk=task(9); 
    if(!isOk) 
    { 
     return; 
    } 
} 

Следует отметить, что я не могу поставить их в петлю (Мой код похож на это, но не именно этот код)

Условный блок очень некрасиво и я могу быть тюк, чтобы написать это следующим образом:

#define TASK(x) {if(!task(x)) return;} 

void function() 
{ 
    TASK(1); 

    // more code here 

    TASK(2); 

    // more code here 

    TASK(3); 

    // more code here 

    TASK(4); 

    // more code here 

    TASK(5); 

    // more code here 

    TASK(6); 

    // more code here 

    TASK(7); 

    // more code here 

    TASK(8); 

    // more code here 

    TASK(9); 
} 

Мой вопрос:

есть ли лучший способ сделать это, когда я использую C++ 11?

Проблема с этим кодом является:

Я не могу отладить его легко.

Макрос не находится внутри пространства имен и, возможно, конфликтует с другими макросами.

Update 1

Поскольку большая часть ответа здесь пытается решить эту проблему в определенном коде, когда я ищу для общего решения, я прошу, специфические вопросы, связанные с этим кодом:

1 - Могу ли я использовать лямбда для имитации макроса?

2- Можно ли использовать constexpr для имитации макроса?

3 Любой другой способ имитировать MACRO в дружественном компиляторе (с тем же результатом, что и макрос), поэтому я могу легко отладить их?

+9

'задачи (1) && задачи (2) && задачи (3) && ...'? –

+1

Помните, что логический оператор '&&' short-circuit. Это означает, что вы можете связать их вместе в большой цепочке '&&', и как только первая функция выйдет из строя, остальные не будут вызваны. –

+0

@KerrekSB: это хорошо, но я не упомянул, что между вызовами есть некоторые другие коды, и я собираюсь обновить свой примерный код, чтобы представить его. К сожалению, ваше решение не подходит. (это причина, по которой я не могу поместить их в цикл, как объяснено в исходном вопросе). – mans

ответ

0

Я бы поставил код для выполнения КСТАТИ вызова задачи в вектор, а затем запустить цикл:

const size_t steps = 9; 
using ops = std::function<void()>; 
std::vector<ops> vops(steps); 
steps[0] = [] { /* some code here to execute after task 0 */ }; 
... 

for(size_t i = 0; i < steps; ++i) { 
    if(!task(i)) return; 
    if(vops[i]) (vops[i])(); 
} 
1

Вместо того, чтобы использовать простой return, вы можете выбрать, чтобы использовать исключение вместо этого, который не только оставить ток но все функции до тех пор, пока они не найдут блок catch.

Что-то вроде этого:

void tryTask(int i){ 
    auto isOk=task(i); 
    if(!isOk) 
    { 
     throw std::runtime_error("Task failed: Nr. "+to_string(i)); 
    } 
} 

function() 
{ 
    tryTask(1); 
    // more code here 
    tryTask(2); 
    // more code here 
    tryTask(3); 
    ... 
} 

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

void callfunction(){ 
    try{ 
     function(); 
    } catch (std::exception& e) { 
     //do whatever happens if the function failed, or nothing 
    } 
} 

Если у вас есть контроль о функции task(), вы также можете решить чтобы исключить исключение непосредственно внутри этой функции вместо возврата bool.

Если вы хотите убедиться, что используете только свои собственные исключения, напишите небольшой класс для этого, используя только информацию, необходимую для обработки исключения (если вам это не нужно, пустой класс выполнит эту работу) и вместо этого перехватите/поймайте экземпляр своего класса.

+1

ваше создание исключения может компилироваться, но определенно не будет предоставлять ожидаемый результат. – Slava

+0

@Slava, если вы имели в виду конкатенацию строк: fixed :) – Anedar

+0

Нет причин, чтобы, если задача возвращает false, это означает ошибку, это просто означает, что нет продолжить обработку и вернуться назад. Если я использую исключение, то любой, кто читает код, может подумать, что это ошибка, когда это не так. Мне интересно, почему нет конструкции в C++ 11 или C++ 14, которые позволяют мне написать функцию, которую компилятор копирует ее так же, как препроцессор MACRO, но будет дружественным к отладке. Можно ли имитировать это с помощью constexr в C++ 11? – mans

2
void function() { 
    if (!task(1)) return; 
    // code here 
    if (!task(2)) return; 
    // more code here 
    if (!task(3)) return; 
    // more code here 
} 

Это небольшой и плотный и не уродливые громоздкие блоки.

Если task(1) намного больше, вы можете положить return; на следующей отступной линии.

0

Вот быстрый и грязный подход с лямбдами.

Предполагая, что это ваша функция задача:

#include <iostream> 

/** Returns 0 on success; any other returned value is a failure */ 
int task(int arg) 
{ 
    std::cout << "Called task " << arg << std::endl; 
    return arg < 3 ? 0 : 1; 
} 

Invoke задачи в цепи следующим образом:

#include <iostream> 

int main() 
{ 
    int result = Chain::start() 
     .and_then([]() -> int {return task(1);}) 
     .and_then([]() -> int {return task(2);}) 
     .and_then([]() -> int {return task(3);}) 
     .and_then([]() -> int {return task(4);}) 
     .and_then([]() -> int {return task(5);}) 
     .and_then([]() -> int {return task(6);}) 
     .and_then([]() -> int {return task(7);}) 
     .and_then([]() -> int {return task(8);}) 
     .and_then([]() -> int {return task(9);}) 
     .result(); 
    std::cout << "Chain result: " << result << std::endl; 
    return result; 
} 

Поскольку задача возвращает успех только при вызове со значением аргумента меньше, чем 3, цепь вызова прекращается, как ожидалось, после 3-го шага:

$ ./monad 
Called task 1 
Called task 2 
Called task 3 
Chain result: 1 

Это предел ntation класса Chain:

class Chain 
{ 
    public: 
     const int kSuccess = 0; 

     Chain() {_result = kSuccess;} 

     static Chain start() { return Chain(); } 

     Chain& and_then(std::function<int()> nextfn) { 
      if(_result == 0) { 
       _result = nextfn(); 
      } 
      return *this; 
     } 

     int result() { return _result; } 

    private: 
     int _result; 
}; 

Я знаю, это выглядит некрасиво и это не-родовым. Но если это общее направление, о котором вы думали, сообщите мне, и мы можем его развить.

+0

Что такое цепочка? это класс в stl? – mans

+0

нет, см. Реализацию Цепочки в конце ответа –

0

Вы можете использовать целую последовательность.

// No task to call without an integer. 
bool function(std::index_sequence<>) { return true; } 

template<std::size_t I, std::size_t... S> 
bool function(std::index_sequence<I, S...>) { 
    return [](){ 
     auto isOk = task(I) 

     if (!isOk) return false; 

     // some code 

     return true; 

    // it will call function with the rest of the sequence only if the lambda return true. 
    }() && function(std::index_sequence<S...>{}); 
} 

void function() { 
    // this call with a integer sequence from 0 to 9 
    function(std::make_index_sequence<10>{}); 
} 

Этот код будет расширяться так же, как если бы вы его пишете руками.

Если код между вызовами task отличается для каждого шага, вы можете использовать кортеж.

auto afterTask = std::make_tuple(
    [](){ std::cout << "after task 0" << std::endl; }, 
    [](){ std::cout << "after task 1" << std::endl; }, 
    [](){ std::cout << "after task 2" << std::endl; }, 
    [](){ std::cout << "after task 3" << std::endl; }, 
    [](){ std::cout << "after task 4" << std::endl; }, 
    [](){ std::cout << "after task 5" << std::endl; }, 
    [](){ std::cout << "after task 6" << std::endl; }, 
    [](){ std::cout << "after task 7" << std::endl; }, 
    [](){ std::cout << "after task 8" << std::endl; }, 
    [](){ std::cout << "after task 9" << std::endl; } 
); 

А затем изменить определение function с:

template<std::size_t I, std::size_t... S> 
bool function(std::index_sequence<I, S...>) { 
    return task(I) && 
      (static_cast<void>(std::get<I>(afterTask)()), true) && 
      function(std::index_sequence<S...>{}); 
}