2016-12-08 2 views
0

Ich habe Code wie folgt aus:Kann ich dies ohne ein Makro (in C++ 11) tun?

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; 
    } 
} 

Es sollte beachtet werden, dass ich sie nicht in einer Schleife setzen kann (Mein Code ist dies ähnlich, aber nicht genau dieser Code)

Der if-Block ist sehr hässlich und ich kann Ballen es wie folgt zu schreiben:

#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); 
} 

Meine Frage ist:

gibt es einen besseren Weg, dies zu tun, wenn ich C++ 11 verwende?

Das Problem mit diesem Code ist:

ich es nicht leicht debuggen können.

Das Makro befindet sich nicht in einem Namespace und möglicherweise Konflikt mit anderen Makros.

Update 1

Wie hier die meisten der Antwort versucht, das Problem in dem spezifischen Code zu lösen, wenn ich für die allgemeine Lösung suchen, ich specifc Fragen zu diesem Code verwandt bin zu fragen:

1 - Kann ich Lambda verwenden, um das Makro nachzuahmen?

2- Kann ich einen consExpr verwenden, um ein Makro nachzuahmen?

3- Jede andere Möglichkeit, ein MACRO compilerfreundlich nachzuahmen (mit dem gleichen Ergebnis wie ein Makro), damit ich sie leicht debuggen kann?

+9

'Aufgabe (1) && Aufgabe (2) && Aufgabe (3) && ...'? –

+1

Denken Sie daran, dass der boolesche Operator '&&' kurzgeschlossen ist. Dies bedeutet, dass Sie diese in einer großen Kette von '&&' zusammenketten können, und sobald die erste Funktion fehlschlägt, wird der Rest nicht aufgerufen. –

+0

@KerrekSB: das ist gut, aber ich erwähnte nicht, dass es andere Codes zwischen aufrufenden Aufgaben gibt, und ich werde meinen Beispielcode aktualisieren, um es zu präsentieren. Also ist deine Lösung leider nicht geeignet. (das ist der Grund, dass ich sie nicht auf eine Schleife setzen kann, wie in der ursprünglichen Frage erklärt) – mans

Antwort

0

Ich würde Code setzen btw auszuführen Aufgabe in einen Vektor aufrufen und dann eine Schleife laufen:

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

Statt eine Ebene return zu verwenden, können Sie Ausnahmen wählen stattdessen zu verwenden, die nicht nur den Strom verlassen Funktion, aber alle Funktionen, bis sie einen Catch-Block finden.

Etwas wie folgt aus:

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); 
    ... 
} 

Dies jedoch lässt Ihre Funktion eine Ausnahme aus, statt nur zurückkehren, wenn eine der Aufgaben gescheitert. Wenn dies nicht das, was Sie wollen, umgeben Sie es entweder in der Funktion mit einem Try-Catch-Block oder rufen Sie es von einer zweiten Funktion wie folgt aus:

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

Wenn Sie die Kontrolle über die task() Funktion haben, können Sie auch entscheiden, um die Ausnahme direkt innerhalb dieser Funktion zu werfen, anstatt einen Bool zurückzugeben.

Wenn Sie sicherstellen möchten, dass Sie nur Ihre eigenen Ausnahmen abfangen, schreiben Sie eine kleine Klasse, die nur die Informationen enthält, die Sie für die Behandlung der Ausnahme benötigen (wenn Sie keine benötigen, wird eine leere Klasse die Aufgabe erledigen) und stattdessen eine Instanz Ihrer Klasse werfen/fangen.

+1

Ihre Erstellung von Ausnahme kann kompilieren, aber würde definitiv nicht erwartet Ergebnis – Slava

+0

@Slava, wenn Sie die Zeichenfolge Verkettung: behoben :) – Anedar

+0

Es gibt keinen Grund, dass, wenn Aufgabe falsch zurückgeben, bedeutet dies einen Fehler, es bedeutet nur nicht Weiterverarbeitung und Rückgabe zurück. Wenn ich eine Ausnahme verwende, kann jeder, der den Code liest, denken, dass es ein Fehler ist, wenn dies nicht der Fall ist. Ich wundere mich, warum es kein Konstrukt in C++ 11 oder C++ 14 gibt, das mir erlaubt, eine Funktion zu schreiben, die den Compiler als einen Präprozessor MACRO kopiert, aber debuggenfreundlich ist. Ist es möglich, dies unter Verwendung von Constexr in c nachzuahmen ++ 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 
} 

Dies ist klein und eng und keine hässlichen klobigen Blöcke.

Wenn task(1) viel größer ist, können Sie return; auf die nächste eingerückte Zeile setzen.

0

Hier ist ein schneller und dreckiger Ansatz mit Lambdas.

dies Unter der Annahme, ist Ihre Aufgabe, Funktion:

#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; 
} 

die Aufgaben in einer Kette Invoke wie folgt:

#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; 
} 

Da die Aufgabe Erfolg gibt nur, wenn sie mit einem Argument-Wert namens weniger als 3, die Aufrufkette stoppt nach dem dritten Schritt, wie erwartet:

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

Dies ist DURCHFÜHRU ntation der Kettenklasse:

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; 
}; 

Ich weiß, es sieht hässlich und es ist nicht generisch. Aber wenn dies die allgemeine Richtung ist, an die Sie gedacht haben, lassen Sie es mich wissen und wir können sie weiterentwickeln.

+0

was ist kette? Ist es eine Klasse in STL? – mans

+0

Nein, siehe die Implementierung von Chain am Ende der Antwort –

0

Sie können eine ganzzahlige Sequenz verwenden.

// 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>{}); 
} 

Dieser Code wird erweitert, als ob Sie ihn von Hand schreiben würden.

Wenn der Code zwischen den Aufrufen von task für jeden Schritt unterschiedlich ist, können Sie ein Tupel verwenden.

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; } 
); 

Und dann die Definition von function mit ändern:

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...>{}); 
}