簡體   English   中英

使自定義范圍v3視圖可管理

[英]make custom range v3 view pipeable

我正在嘗試使用范圍v3實現屏蔽范圍視圖。 不知怎的,我結束了我執行的情況

ranges::view::masker(datarange, mask)

工作,但管道版本

ranges::view::all(datarange) | ranges::view::masker(mask)

但是,盡管有內部結構的operators ,但是掩模正確到達。 (我將我的masker實現放入ranges::view命名空間,盡管它不是范圍v3的一部分)。

我的測試程序相對簡單,創建一些小部件和無意義的掩碼

class Widget
{
private:
  int   m_int{0};

public:
  Widget() {}
  Widget( int i ) : m_int( i ) {}
  int   the_int() const { return m_int; }
};

inline std::ostream& operator<<( std::ostream& str, const Widget& obj )
{
  str << '\t' << obj.the_int();
  return str;
}

int main()
{
  std::vector<Widget> widgets;
  std::vector<bool>   mask;

  for ( auto i : ranges::view::indices( 24 ) ) {
    widgets.emplace_back( i );
    mask.push_back( i % 3 != 1 );
  }

  std::cout << "wrapped" << std::endl;
  for ( auto& el : ranges::view::masker( widgets, mask ) ) {
    std::cout << el << std::endl;
  }
  std::cout << std::endl;
  std::cout << std::endl;

  std::cout << "piped" << std::endl;
  for ( auto& el : ranges::view::all( widgets ) | ranges::view::masker( mask ) ) {
    std::cout << el << std::endl;
  }

  return 0;
}

忽略命名空間和調試打印輸出, masker只是將數據范圍和掩碼壓縮在一起,對掩碼進行過濾並將窗口小部件作為視圖返回:

        struct mask_fn
        {
            template<typename Rng, typename Msk>
            auto operator()(Rng&& rng, Msk&& msk) const
            {
                CONCEPT_ASSERT(Range<Rng>());
                CONCEPT_ASSERT(Range<Msk>());

                return ranges::view::zip(std::forward<Rng>(rng),
                                         std::forward<Msk>(msk)) |
                       ranges::view::filter([](auto&& range_item) -> bool {

                           return range_item.second;
                       }) |
                       ranges::view::transform(
                           [](auto&& range_item) -> decltype(auto) {
                               return range_item.first;
                           });
            }                  
            template<typename Msk>
            auto operator()(Msk&& msk) const -> decltype(
                make_pipeable(std::bind(*this, std::placeholders::_1,
                                        protect(std::forward<Msk>(msk)))))
            {   
                CONCEPT_ASSERT(Range<Msk>());
                return make_pipeable(
                    std::bind(*this,
                              std::placeholders::_1,
                              protect(std::forward<Msk>(msk))));
            }                 
        };                    

        RANGES_INLINE_VARIABLE(mask_fn, masker)

上述程序打算兩次打印相同的結果范圍,但我只得到:

wrapped
    0
    2
    3
    5
    6
    8
    9
    11
    12
    14
    15
    17
    18
    20
    21
    23


piped

因此,當使用auto operator()(Rng&& rng, Msk&& msk) const ,正確的小部件循環,具有auto operator()(Msk&& msk) const不會返回任何內容。

我嘗試將一些調試打印輸出添加到前者(因為它最終被后者調用)並觀察到掩碼正確到達。

        struct mask_fn
        {
            template<typename Rng, typename Msk>
            auto operator()(Rng&& rng, Msk&& msk) const
            {
                CONCEPT_ASSERT(Range<Rng>());
                CONCEPT_ASSERT(Range<Msk>());
                for(auto t :
                    ranges::view::zip(rng, msk) |
                        ranges::view::filter([](auto&& range_item) ->
                        bool {
                            return range_item.second;
                        }) |
                        ranges::view::transform(
                            [](auto&& range_item) -> decltype(auto) {
                                return range_item.first;
                            }))
                    std::cout << "w: " << t << std::endl;
                return ranges::view::zip(std::forward<Rng>(rng),
                                         std::forward<Msk>(msk)) |
                       ranges::view::filter([](auto&& range_item) -> bool {
                           std::cout << "checking widget "
                                     << range_item.first << std::endl;
                           std::cout << "returning " << range_item.second
                                     << std::endl;
                           return range_item.second;
                       }) |
                       ranges::view::transform(
                           [](auto&& range_item) -> decltype(auto) {

                               return range_item.first;
                           });
            }
            template<typename Msk>
            auto operator()(Msk&& msk) const -> decltype(
                make_pipeable(std::bind(*this, std::placeholders::_1,
                                        protect(std::forward<Msk>(msk)))))
            {
                CONCEPT_ASSERT(Range<Msk>());
                return make_pipeable(
                    std::bind(*this,
                              std::placeholders::_1,
                              protect(std::forward<Msk>(msk))));
            }
        };

        RANGES_INLINE_VARIABLE(mask_fn, masker)

(稍微削減輸出)可以看到使用operator()假定的返回范圍循環遍歷正確的小部件,但是返回行中lambda內的打印輸出顯示所有項目的“假”標記。

wrapped
w:  0
w:  2
w:  3
w:  5
<snap>
w:  20
w:  21
w:  23
checking widget     0
returning 1
    0
checking widget     1
returning 0
checking widget     2
returning 1
    2
checking widget     3
returning 1
    3
<snap>
checking widget     22
returning 0
checking widget     23
returning 1
    23


piped
w:  0
w:  2
w:  3
w:  5
<snap>
w:  20
w:  21
w:  23
checking widget     0
returning 0
checking widget     1
returning 0
checking widget     2
returning 0
checking widget     3
returning 0
<snap>
checking widget     22
returning 0
checking widget     23

我現在最好的猜測是我把protectstd::forward&&std::move搞砸了,盡管我試圖盡可能地靠近filter.hpp (因為我認為我已經合理地理解了它)好吧)並嘗試了一些隨機添加/刪除&符號和轉發沒有成功。

有任何建議如何解決這個問題? (理想情況下,解釋正在發生的事情?)。

提前致謝。

腳注:我現在不關心c ++ 11的兼容性。

編輯:

我把這個爛攤子推到了github

std::bind很奇怪。 如果將bind_expression (調用std::bind的結果傳遞給std::bind ,它會生成一個從“葉子”向下計算的表達式樹。 例如:

auto f = [](int i, int j){ return i * j; };
auto g = [](int i) { return i + 1; };
auto b = std::bind(f, std::bind(g, std::placeholders::_1), std::placeholders::_2);
std::cout << b(0, 3) << '\n'; // prints 3

這里的調用b(0, 3)相當於f(g(0), 3)

protect是一個range-v3實用程序,用於捕獲bind對象內部的函數對象,如果這些函數對象恰好是bind_expression ,則阻止std::bind接受這種怪異。 對於非bind_expressionsprotect具有“按值捕獲rvalues和按引用lvalues”行為(range-v3中的大多數事情假設調用者保證左值的生命周期,但rvalues可能在需要之前“消失”,因此必須存儲)。

不幸的是,在“部分應用程序”重載中使用帶有Range protect

template<typename Msk>
auto operator()(Msk&& msk) const -> decltype(
    make_pipeable(std::bind(*this, std::placeholders::_1,
                            protect(std::forward<Msk>(msk)))))
{   
    CONCEPT_ASSERT(Range<Msk>());
    return make_pipeable(
        std::bind(*this,
                  std::placeholders::_1,
                  protect(std::forward<Msk>(msk))));
}

std::bind與range-v3有不同的設計:它存儲在返回的bind_expression傳遞的任何內容的副本,並在調用時將表示這些存儲對象的lvalues傳遞給包裝函數。 最終結果是你的重載返回一個包含在bind_expression中的make_pipeable ,它包含調用者傳入的vector的副本。

當測試程序在另一個范圍內“管道”時, make_pipeable調用該范圍內的另一個重載,以及一個左值,表示存儲在綁定表達式中的vector副本。 您將該左值傳遞給view::zip ,它(如上所述)假定其調用者將保證只要它生成的zip_view ,左值將保持活動狀態。 當然不是這種情況: make_pipeable臨時 - 包括存儲在其包含的bind_expressionvector - 在測試程序中的range-for語句中評估初始化程序后被銷毀。 當range-for試圖訪問該死vector ,發生UB,在這種情況下表現為空范圍。

修復是在你的“部分應用程序”重載中不使用protect ,而是將范圍的all_view傳遞給std::bind

template<typename Msk>
auto operator()(Msk&& msk) const -> decltype(
    make_pipeable(std::bind(*this, std::placeholders::_1,
                            ranges::view::all(std::forward<Msk>(msk)))))
{   
    CONCEPT_ASSERT(Range<Msk>());
    return make_pipeable(
        std::bind(*this,
                  std::placeholders::_1,
                  ranges::view::all(std::forward<Msk>(msk))));
}

(不可否認,如果protect可以通過拒絕接受Range來防止此錯誤,那將是很好的。)

經過多次嘗試后,我們發現std::bind應該接收到掩碼的std::ref

            return make_pipeable(
                std::bind(*this,
                          std::placeholders::_1,
                          std::ref(msk));

如果沒有,那么 - 所以我的理解 - masker將比msk的臨時副本壽命更長。

暫無
暫無

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

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