有哪些随机访问容器类型可以接受预填充和预分配缓冲区?

10

我即将自己编写一些代码,因为没有找到我需要的东西,但我觉得在深入研究之前应该先向大众询问。

  • 是否有boost或stl的随机访问容器类型,允许传入预填充的缓冲区?

这个想象中的容器类型将操作类似于这样:

  const int blobSize = unpackBlobSize( msg );
  int *     blob     = unpackBlobData( msg );

  SpecialVector<int> mySpecialIntVector( blob, blobSize );

我需要将一个旧的库中使用C风格的原始指针来操作缓冲区,但希望使用C++样式的容器语义而不需要进行复制。我希望能够使用std::vector加上预分配和预填充缓冲区构造函数,但要去除resize方法。

Libeigen通过他们的Eigen::Map提供了这种功能,可以实现以下功能:

  int array[9];
  for(int i = 0; i < 9; ++i) 
      array[i] = i;
  stl::cout << Eigen::Map<Eigen::Matrix3i>(array) << stl::endl;

有人知道一个覆盖这些限制的 boost 或 stl 模板吗?


7
我不是非常熟悉这个库,但我相信 Boost.Range 可能有你可以使用的东西。据我所知,它提供了一些薄包装器,使迭代器对看起来像容器一样。 - Dennis Zickefoose
@Dennis,那似乎是一个真正的解决方案。正在调查中。 - Catskul
我不明白为什么会有这种需求。std::vector 尽可能地模拟标准数组,同时还具有隐式重新分配的功能。如果你要求“减少大小”,那么你还剩下什么?如果你想使用 std 算法,你可以将指针作为迭代器传递。你能举个例子说明为什么你需要在固定缓冲区上使用向量 API 吗?话虽如此,我认为在 std::string 的情况下,这将非常有用。 - davka
3
这就是我的观点,我没有看到一个例子。如果 vector API 中没有自动调整大小功能,那你缺少什么?“更加安全的 C++/STL 方式”只是一种说法,抱歉。你可以像使用内置数组一样高兴地超出 vector 的边界。唯一比 vector 更安全的是隐式内存管理,但你已经放弃了这一点。指针只是一种(内置)迭代器类型,只要你不自己管理内存,就可以将它们用作迭代器。STL 只是一种工具,而不是避免不良后果的护身符 :) - davka
@davka 让我们在聊天室里继续这个讨论 - Catskul
显示剩余8条评论
3个回答

8

根据Dennis Zickenfoose的评论,我查了一下Boost.range似乎提供了完美的解决方案:

  #include <boost/range.hpp>
  #include <boost/foreach.hpp>
  #include <iostream>

  int main()
  {
    const int blobSize = 100;
    int blob[blobSize];

    for( int i = 0; i < blobSize; ++i )
      blob[i] = i;

    boost::iterator_range<int*> blobPsudoContainer( blob, blob + blobSize );

    BOOST_FOREACH( int & i, blobPsudoContainer )
      i = i + 1;

    std::cout << "Size is:" << blobPsudoContainer.size() << "\n";
    std::cout << "value at 0 is:" << blobPsudoContainer[0] << "\n";
    return 0;
  }

感谢Dennis! : )

3
不错。我认为我没有想到过使用boost::range来完成这个特定的目的。我一直认为它仅用于表示容器的一个子集 - 将其应用于C数组以将数组转换为“容器”根本没有进入我的脑海。 - Michael Burr
现在我有些贪心了...我在想是否可以将其设置为自动转换std容器类型,这样我就可以让所有模板函数签名都采用boost.range类型,并免费覆盖所有基础:) - Catskul
1
@Catskul: Ranges不是类型,它们是_concepts_。boost::iterator_range是实现range concept的一种类型;它恰好由两个用户提供的迭代器定义。您也可以实现满足range concept的类型。所有标准容器也恰好满足range concept。 - Nicol Bolas
@Nicol,“Ranges不是类型,它们是概念。”是的,我明白。如果我不明白,我们就不会有这个对话 ;) 如果您希望我更详细一些,我想说我可以编写模板化函数,自动从std容器类型实例化boost ranges,以便我处理std::container类型和ranges而无需进行特殊化。 - Catskul
@Catskul:但是你不能从任何东西实例化范围,因为范围是概念而且概念是不能被实例化的。此外,Boost.Range已经包含了许多处理范围类型的模板算法。它们还具有延迟评估功能,因此您可以高效地将多个算法组合成一个算法。标准容器已经符合范围标准,因此任何接受范围概念的模板函数都将接受标准容器。 - Nicol Bolas
@Catskul:我觉得Nicol并没有让我感到轻蔑。我认为这只是你的想法;-] - ildjarn

4

并不是你想要的东西,这只是语言的不足。如果您满意于让分配器静态地猜测良好的预期最大元素计数,则可以在分配器内嵌入普通C数组,并尽可能地延迟使用它;这通常称为自动缓冲区。


旧回答:

所有标准库容器都允许您指定一个分配器。您可以创建一个缓冲区分配器。


非常有趣的想法。不过我仍然会面临调整大小的问题。 - Catskul
@Catskul 当进行调整大小时,它将使用您的自定义分配器。 - young
4
他的意思是他希望提供的缓冲区已经包含初始化值;标准库容器会假设分配器返回未初始化的内存,并覆盖现有的数据。 - Dennis Zickefoose
我本来要写的,但现在我们应该澄清一下:@Cat:你想传递缓冲区,还是只是假设某种预期的静态缓冲区。你为什么要动态分配缓冲区? - GManNickG
1
@Catskul:也许我错过了什么,但是为什么不直接开始使用您的缓冲区呢?将其放入std::vector<>中,然后不使用std::vector功能有什么好处? - GManNickG
显示剩余5条评论

0

我想我会发布我最终使用的自定义解决方案,以备后人参考,或者如果有人感兴趣或有任何评论,请原谅紧凑的格式;这是为了消除水平滚动条。

如果您使用此解决方案,请在评论部分让我知道。我很想听听它是否对其他人有用。

我们需要以下标题:

 #include <boost/range/iterator_range.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_array.hpp>
 #include <vector>

一个帮助程序,用于挂接到现有的引用计数对象

 template<typename RefCountedTypeT>
 struct RefCountLinkFunctor
 {
   public:
     RefCountLinkFunctor( const RefCountedTypeT & ptr ) 
       : m_ptr(ptr) 
     { 
       &RefCountedTypeT::use_count; // try to check if refcounted
      } 

      template<typename T> void operator()(T*) {}

   protected:
     RefCountedTypeT m_ptr;
 };

然后:

 template< typename DataT >
 class DataRange : public boost::iterator_range< DataT * >
 {
 public:
   typedef boost::iterator_range< DataT * >    ParentType;
   typedef DataRange< DataT >                  OwnType;
   typedef DataT                               DataType;
   typedef boost::iterator_range< DataType * > Range;
   typedef boost::shared_ptr< OwnType >        Ptr;
   typedef std::vector<DataType>               DataVector;
   typedef boost::shared_ptr<DataVector>       DataVectorPtr;

   static Ptr allocateShared( std::size_t size )
   {
     typedef boost::shared_array<DataType>         DataSharedArray;
     typedef RefCountLinkFunctor<DataSharedArray>  Deleter;

     DataSharedArray   dataArray( new DataType[size] );
     Deleter           deleter( dataArray );

     DataT * begin = dataArray.get();
     DataT * end   = begin + size;

     return Ptr( new OwnType(begin, end), deleter);
   }

   template< typename RefCountedT >
   static Ptr 
   makeShared( DataType * begin, DataType * end, const RefCountedT & refObj )
   {
     RefCountLinkFunctor< RefCountedT >  deleter( refObj );
     return Ptr( new OwnType(begin, end), deleter );
   }

   template< typename RefCountedT, typename InDataT >
   static Ptr 
   makeShared( DataVector & data, const RefCountedT & refObj )
   {
     RefCountLinkFunctor< RefCountedT >  deleter( refObj );

     DataType * begin = &data.front();
     DataType * end   = begin + data.size();

     return makeShared( begin, end, deleter );
   }

   static Ptr makeShared( const DataVectorPtr & data )
   {
     typedef boost::shared_ptr< std::vector<DataType> > InputVectorPtr;

     RefCountLinkFunctor< InputVectorPtr >  deleter( data );

     DataType * begin = &data->front();
     DataType * end   = begin + data->size();

     return makeShared( begin, end, deleter );
   }

 protected:
     DataRange( DataType * begin, DataType * end ) : ParentType( begin, end ){}

};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接