创建库以覆盖迭代器的运算符*() - 冒险悬空指针 [英] Create library to override operator*() of iterator - risk dangling pointer

查看:203
本文介绍了创建库以覆盖迭代器的运算符*() - 冒险悬空指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建自己的 boost :: adapters :: transformed

I am trying to create my own boost::adaptors::transformed.

以下是相关的提升代码

以下是它的用法(修改自 LogicStuff的答案): -

Here is its usage (modified from a SO answer by LogicStuff):-

C funcPointer(B& b){ 
    //"funcPointer" is function convert from "B" to "C"
    return instance-of-C
}

MyArray<B> test;  //<-- any type, must already have begin() & end()

for(C c : test | boost::adaptor::transformed(funcPointer)) {
    //... something ....
}

结果将与: -

for(auto b : test) {
    C c = funcPointer(b);
    //... something ...
}



我的尝试



我创建了 CollectAdapter ,其目的是像 boost :: adapter :: transformed

在大多数情况下都可以正常工作。

My Attempt

I created CollectAdapter that aim to work like boost::adaptor::transformed.
It works OK in most common cases.

以下是完整的 演示 备份 (与下面的代码相同)

Here is the full demo and back up. (same as below code)

有问题的部分是 CollectAdapter - 我的库的核心。< br>
我不知道是否应该缓存集合_ 按指针按值

The problematic part is CollectAdapter - the core of my library.
I don't know whether I should cache the collection_ by-pointer or by-value.

CollectAdapter 封装基础集合_ (例如指向 std :: vector<> ): -

CollectAdapter encapsulates underlying collection_ (e.g. pointer to std::vector<>) :-

template<class COLLECTION,class ADAPTER>class CollectAdapter{
    using CollectAdapterT=CollectAdapter<COLLECTION,ADAPTER>;
    COLLECTION* collection_;    //<---- #1  problem? should cache by value?
    ADAPTER adapter_;           //<---- = func1 (or func2)
    public: CollectAdapter(COLLECTION& collection,ADAPTER adapter){
        collection_=&collection;
        adapter_=adapter;
    }
    public: auto begin(){
        return IteratorAdapter<
            decltype(std::declval<COLLECTION>().begin()),
            decltype(adapter_)>
            (collection_->begin(),adapter_);
    }
    public: auto end(){ ..... }
};

IteratorAdapter (在上面使用)封装底层迭代器,改变 operator * 的行为: -

template<class ITERATORT,class ADAPTER>class IteratorAdapter : public ITERATORT {
    ADAPTER adapter_;
    public: IteratorAdapter(ITERATORT underlying,ADAPTER adapter) :
        ITERATORT(underlying),
        adapter_(adapter)
    {   }
    public: auto operator*(){
        return adapter_(ITERATORT::operator*());
    }
};

CollectAdapterWidget (在下面使用)只是一个帮助类来构建 CollectAdapter -instance。

CollectAdapterWidget (used below) is just a helper class to construct CollectAdapter-instance.

它可以像: -

int func1(int i){   return i+10;   }
int main(){
    std::vector<int> test; test.push_back(5);
    for(auto b:CollectAdapterWidget::createAdapter(test,func1)){
        //^ create "CollectAdapter<std::vector<int>,func1>" instance
         //here, b=5+10=15
    }
}  



问题



上述代码在大多数情况下都可以正常工作,除非 COLLECTION 是一个临时对象。

更具体地说,当我创建适配器适配器 适配器 时,可能会发生悬空指针。

More specifically, dangling pointer potentially occurs when I create adapter of adapter of adapter ....

int func1(int i){   return i+10;    }
int func2(int i){   return i+100;   }
template<class T> auto utilityAdapter(const T& t){
    auto adapter1=CollectAdapterWidget::createAdapter(t,func1);
    auto adapter12=CollectAdapterWidget::createAdapter(adapter1,func2);
    //"adapter12.collection_" point to "adapter1"
    return adapter12;
    //end of scope, "adapter1" is deleted
    //"adapter12.collection_" will be dangling pointer
}
int main(){
    std::vector<int> test;
    test.push_back(5);
    for(auto b:utilityAdapter(test)){
        std::cout<< b<<std::endl;   //should 5+10+100 = 115
    }
}

这个会导致运行时错误。这是 悬空指针演示

This will cause run time error. Here is the dangling-pointer demo.

在实际使用中,如果界面更棒,例如使用 | 运算符,将更难检测到错误: -

In the real usage, if the interface is more awesome, e.g. use | operator, the bug will be even harder to be detected :-

//inside "utilityAdapter(t)"
return t|func1;        //OK!
return t|func1|func2;  //dangling pointer



问题



如何改进我的库以修复此错误,同时保持性能& 健壮性& 维持性接近同一级别?

Question

How to improve my library to fix this error while keeping performance & robustness & maintainablilty near the same level?

换句话说,如何缓存 COLLECTION (可以适配器真实数据结构)优雅?

In other words, how to cache data or pointer of COLLECTION (that can be adapter or real data-structure) elegantly?

或者,如果是从头开始编码比修改我的代码更容易回答,去吧。 :)

Alternatively, if it is easier to answer by coding from scratch (than modifying my code), go for it. :)

当前代码通过指针缓存 。 >
解决方法的主要思想是缓存按值

The current code caches by pointer.
The main idea of workarounds is to cache by value instead.

适配器缓存 COLLECTION

以下是主要变化: -

Let adapter cache the value of COLLECTION.
Here is the main change:-

COLLECTION collection_;    //<------ #1 
//changed from   .... COLLECTION* collection_;

缺点: -


  • 整个数据结构(例如 std :: vector )将被复制值 - 浪费资源。

    (直接用于 std :: vector

  • Whole data-structure (e.g. std::vector) will be value-copied - waste resource.
    (when use for std::vector directly)

我将创建2个版本的库 - AdapterValue AdapterPointer

我必须创建相关的类( Widget AdapterIterator 等。)。

I will create 2 versions of the library - AdapterValue and AdapterPointer.
I have to create related classes (Widget,AdapterIterator,etc.) as well.


  • AdapterValue - 按值。 (专为 utilityAdapter()而设计)

  • AdapterPointer - by指针即可。 (专为 std :: vector 而设计)

  • AdapterValue - by value. (designed for utilityAdapter())
  • AdapterPointer - by pointer. (designed for std::vector)

缺点: -


  • 重复代码=低可维护性

  • 用户(编码员)必须非常清楚选择哪一个=低稳健性

我可以使用模板专精来执行此操作: -

I may use template specialization that do this :-

If( COLLECTION is an "CollectAdapter" ){ by value }  
Else{ by pointer }    

缺点: -


  • 许多适配器类之间不合作。

    他们必须互相认识:认可 =应按值隐藏

  • Not cooperate well between many adapter classes.
    They have to recognize each other : recognized = should cache by value.

很抱歉很长的帖子。

推荐答案

我个人会选择模板专业化–但是,不是专门化原始模板,而是专门设置嵌套类:

I personally would go with template specialisation – however, not specialise the original template, but a nested class instead:

template<typename Collection, typename Adapter>
class CollectAdapter
{
    template<typename C>
    class ObjectKeeper // find some better name yourself...
    {
        C* object;
    public:
        C* operator*() { return object; };
        C* operator->() { return object; };
    };
    template<typename C, typename A>
    class ObjectKeeper <CollectAdapter<C, A>>
    {
        CollectAdapter<C, A> object;
    public:
        CollectAdapter<C, A>* operator*() { return &object; };
        CollectAdapter<C, A>* operator->() { return &object; };
    };

    ObjectKeeper<Collection> keeper;

    // now use *keeper or keeper-> wherever needed
};

外部类通过只使用指针来覆盖这两种情况,而嵌套类隐藏了差异。

The outer class then covers both cases by just always using pointers while the nested class hides the differences away.

当然,不完整(你需要添加适当的构造函数,例如,外部和内部类),但它应该给你的想法...

Sure, incomplete (you need yet need to add appropriate constructors, for instance, both to outer and inner class), but it should give you the idea...

您甚至可以允许用户选择他/她想要复制:

You might even allow the user to select if he/she wants to copy:

template<typename Collection, typename Adapter, bool IsAlwaysCopy = false>
class CollectAdapter
{
    template<typename C, bool IsCopy>
    class ObjectWrapper // find some better name yourself...
    {
        C* object;
    public:
        C* operator*() { return object; };
        C* operator->() { return object; };
    };
    template<typename C>
    class ObjectWrapper<C, true>
    {
        C object;
    public:
        C* operator*() { return &object; };
        C* operator->() { return &object; };
    };

    // avoiding code duplication...
    template<typename C, bool IsCopy>
    class ObjectKeeper : public ObjectWrapper<C, IsCopy>
    { };
    template<typename C, typename A, bool IsCopy>
    class ObjectKeeper <CollectAdapter<C, A>, IsCopy>
        : public ObjectWrapper<CollectAdapter<C, A>, true>
    { };

    ObjectKeeper<Collection> keeper;
};

这篇关于创建库以覆盖迭代器的运算符*() - 冒险悬空指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆