簡體   English   中英

解決C ++中的循環引用變量依賴關系

[英]Resolving circular reference variable dependency in C++

假設我有以下代碼用於使用C ++制作數據流體系結構

class DataflowClass {
    public:
        DataflowClass(int & _a, int & b) : a(_a), b(b){}

        inline void calculate(){
            c = a + b;
            d = a - 2*b;
        }

        inline int & getC() { return c; }
        inline int & getD() { return d; }

    private:
        int c = 1;
        int d = 2;
        int & a;
        int & b;
};

void TestReferenceVariableDataflow(int count){

    int a;
    int b;

    DataflowClass c1(a, b);
    DataflowClass c2(c1.getC(), c1.getD());
    DataflowClass c3(c2.getC(), c2.getD());

    for(int i=0;i<count;i++){
        a = c3.getC();
        b = c3.getD();
        c1.calculate();
        c2.calculate();
        c3.calculate();
    }
}

DataflowClass是具有兩個輸入和兩個輸出的數據流塊的主要構造。 DataflowClass的構造函數接受兩個引用變量作為輸入,並具有兩個getter函數,這些引用返回對該引用的兩個輸出成員,可以看出,在TestReferenceVariableDataflow函數中,我構造了3個DataflowClass實例,而c1實例構造了對兩個局部變量的引用在TestReferenceVariableDataflow函數中定義的變量中,已經使用來自c1.getC()和c2.getC()的兩個引用構造了c2,反之亦然。

在每次迭代中,將3類的計算方法稱為定義順序,這將導致3類內的數據流計算。

如您所見,真正的問題是循環依賴性,它是通過引用TestReferenceVariableDataflow函數中定義的兩個變量構造的c1,並且在每次迭代結束時,我必須從c3.getC()和c3.getD()復制值這些變量。

由於DataflowClass在其構造函數中接受引用變量,因此我無法將c3的輸出變量直接連接到c1,因為c3實例在c1的構造時不可用,因此我必須定義兩個額外的變量來復制從最后到第一次的每次迭代中的值。

如您所知,具有引用成員的類應在構造時初始化引用,此問題使該循環依賴成為問題。

這個循環依賴問題有什么解決方案嗎?

編輯這個設計背后有一些簡單的原因,

1-首先,這是設計,僅使用C ++通用語法,不使用任何其他類型的庫,甚至不使用std :: *,只是出於性能考慮,認為其中有數十個這些塊,並且所有塊都是串行連接的,另一個避免使用庫的原因是該代碼是實時軟件的一部分,其行為比庫更容易預測

2-DataflowClass的實例永遠不會破壞,我們不能有懸空的指針

3-在時間緊迫且實時的軟件中,由於不可預測性而以某種方式禁止動態內存管理,並且該設計確實可以用於所有靜態內存分配

4-指針的使用不被認為是一個好的解決方案,因為使用指針無法實現靜態的編譯時斷言,編譯時的接線可防止未連接的輸入網絡,而且指針可能會為null或類似的東西

5-這種設計不需要為每個變量更改信號,因為所有計算方法都將被串行調用,並且也可能與父類一起自動調用

6-塊的輸入可能來自不同的塊(與其之前的塊不同),所提供的示例只是一個簡單的情況,其中塊彼此均勻地連接

7-簡單的設計是simulink塊的C ++實現,塊的輸出連接到其他塊的輸入,它們將隨着時間的增加計算其輸出

您可以使用指針而不是引用:

class DataflowClass {
public:
    DataflowClass() = default;

    DataflowClass(int& pa, int& pb) : a(&pa), b(&pb){}
    void linkTo(int& pa, int& pb) { a = &pa; b = &pb; }

    void calculate(){
        assert(a != nullptr && b != nullptr);
        c = *a + *b;
        d = *a - 2 * *b;
    }

    int & getC() { return c; }
    int & getD() { return d; }

private:
    int c = 1;
    int d = 2;
    const int* a = nullptr;
    const int* b = nullptr;

};

void TestReferenceVariableDataflow(int count)
{
    DataflowClass c1;
    DataflowClass c2(c1.getC(), c1.getD());
    DataflowClass c3(c2.getC(), c2.getD());
    c1.linkTo(c3.getC(), c3.getD());

    for(int i=0;i<count;i++){
        c1.calculate();
        c2.calculate();
        c3.calculate();
    }
}

但是指針可能為空:-/

具有以下內容似乎更簡單:

std::pair<int, int> calculate(int a, int b) { return { a + b, a - 2 * b} ; }

void TestReferenceVariableDataflow(int count)
{
    int a = 1;
    int b = 2;

    for(int i=0;i<count;i++){
        std::tie(a, b)  = calculate(a, b);
        std::tie(a, b)  = calculate(a, b);
        std::tie(a, b)  = calculate(a, b);
    }
}

我能明智地看到僅使用引用的方法是唯一的方法是完全分離存儲。

class DataFlowClass {
    const int& a;
    const int& b;
    int& c;
    int& d;

public:
    DataFlowClass(const int& a, const int& b, int& c, int& d) : a{a}, b{b}, c{c}, d{d} {
        c = 1;
        d = 2;
    }

    void calculate() {
        c = a + b;
        d = a - 2*b;
    }
};

void TestReferenceVariableDataflow(int count){
    int storage[6]; // could use 6 int variables instead

    DataflowClass c1(storage[0], storage[1], storage[2], storage[3]);
    DataflowClass c2(storage[2], storage[3], storage[4], storage[5]);
    DataflowClass c3(storage[4], storage[5], storage[0], storage[1]);

    for(int i=0;i<count;i++){
        c1.calculate();
        c2.calculate();
        c3.calculate();
    }
}

但是,在此,調用方有責任確保正確設置所有引用,這可能會變得很復雜。

既然您提到這是針對嵌入式環境的,請注意,此解決方案可能需要Jarod42的答案中基於指針的解決方案所需的內存量的1.5倍。

您的設計過於復雜,沒有太多理由:

class DataflowClass {
    public:
        void calculate(const DataflowClass &from ){
            c = from.getC() + from.getD();
            d = from.getC() - 2*from.getD();
        }

        int getC() const { return c; }
        int getD() const { return d; }

    private:
        int c = 1;
        int d = 2;
};

void TestReferenceVariableDataflow(int count){
    std::array<DataflowClass,3> c;

    for(int i=0;i<count;i++){
        for( size_t i = 0; i < c.size(); ++i ) {
            auto prev = ( i == 0 ? c.size() : i ) - 1;
            c[i].calculate( c[prev] ); 
        }
    }
}

這樣做的方式相同,但更通用,更安全。

終於找到了使用std :: reference_wrapper的最佳解決方案

class DataflowClass {
    public:
        DataflowClass(){}

        inline void setInputs(int & _a, int & _b){
            this->a = _a ;
            this->b = _b;
        }

        inline void calculate(){
            c = a + b;
            d = a - 2*b;
        }

        inline int & getC() { return c; }
        inline int & getD() { return d; }

    private:
        int c = 1;
        int d = 2;
        int a_initial = 0;
        int b_initial = 0;
        std::reference_wrapper<int> a = a_initial;
        std::reference_wrapper<int> b = b_initial;
};

void TestReferenceVariableDataflow(int count){

    DataflowClass c1, c2, c3;
    c2.setInputs(c1.getC(), c1.getD());
    c3.setInputs(c2.getC(), c2.getD());
    c1.setInputs(c3.getC(), c3.getD());

    for(int i=0;i<count;i++){
        c1.calculate();
        c2.calculate();
        c3.calculate();
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM