C ++ 11 Smart Pointer performs functions for mutable objects

I have a class with no public constructor, but a static factory or builder method, and the size of the object being created depends on the arguments passed to the factory (or builder).

Is there a way to create a generic (or unique) pointer to such an object using std::make_shared

(or std::make_unique

)?

For any proposed polymorphism, I prefer templates over virtual methods.

+3


source to share


3 answers


I have a class without a public constructor ...

Is there a way to create a generic (or unique) pointer to such an object using std::make_shared

(or std::make_unique

)?

There is no such way. std::make_shared

and std::make_unique

a public constructor is required.

Instead, you can create a generic (unique) pointer without the convenience "make" function:



// within the factory
return std::shared_ptr<T>(new T(size));

      


PS. The C ++ 11 standard library has no std::make_unique

.

+3


source


If you have a factory method in place that will create an object in the block of memory you provide it, plus a method that tells you how much space you need, you can do that.

I'm going to be free to use C ++ 14, add your own typename

and ::type

if you really need C ++ 11.

First, suppose we have:

struct some_args {
  // whatever
};
std::size_t how_big_is_X( some_args );
X* make_X( some_args, void* buffer );

      

with the above, I can do something functionally equivalent make_shared

.

template<std::size_t Sz, std::size_t align=alignof(void*)>
struct smart_buffer_t {
  void(*dtor)(void*) = 0;
  std::aligned_storage_t< Sz, align> data;
  template<class T, class...Args>
  T* emplace(Args&&...args) {
    return ctor( [&](void* pdata) {
      ::new( pdata ) T(std::forward<Args>(args)...);
    } );
  }
  template<class F>
  T* ctor( F&& f ) {
    std::forward<F>(f)( (void*)&data );
    dtor = [](void* ptr){
      static_cast<T*>(ptr)->~T();
    };
    return static_cast<T*>((void*)&data);
  }
  ~smart_buffer_t() {
    if (dtor) dtor(&data);
  }
};

template<class T, std::size_t Sz, std::size_t Algn=alignof(void*), class F>
std::shared_ptr<T>
make_sized( F&& f ) {
  auto pbuff = std::make_shared<smart_buffer_t<Sz, Algn>>();
  T* r = pbuff->ctor( std::forward<F>(f) );
  return {r, pbuff}; // aliasing ctor
}

      



now we have:

template<std::size_t I>
using index_t = std::integral_constant<std::size_t, I>;
template<std::size_t I>
using pow_t = index_t< (1<<I) >;

std::shared_ptr<X>
make_shared_X( some_args args ) {
  std::size_t Sz = how_big_is_X(args);
  using pmaker = std::shared_ptr<X>(*)(some_args);
  using maker_maker = [](auto Sz){
    return +[](some_args args) {
      return make_sized<X, Sz>([&](void* ptr){
        return make_X(args, ptr);
      });
    };
  };
  static const pmaker table[] = {
    maker_maker(pow_t<0>{}),
    maker_maker(pow_t<1>{}),
    maker_maker(pow_t<2>{}),
    // ...
    maker_maker(pow_t<63>{}), // assuming 64 bit size_t.
  };
  std::size_t i = 0;
  while(Sz > (1<<i))
    ++i;
  return table[i](args);
}

      

or somesuch.

The code is not verified. It allocates a power of 2 or more than your args demand. But the object is built in the same distribution as the reference counting block.

Any series can be used instead of powers of 2, but the size of the table must be large enough to handle the largest possible return value from how_big_is_X

.

+2


source


#include <memory>
#include <vector>
#include <iostream>

template <typename T>
std::unique_ptr<T> buildObjectWithSize(std::size_t size)  {
    return std::unique_ptr<T>{T::buildObject(size)};
}

class MyObject {
public:
    static MyObject* buildObject(std::size_t size) {
        return new MyObject(size);
    }

    int& operator[](int i) {
        return int_vector[i];
    }
private:
    MyObject(std::size_t size)
    : int_vector(size) {
    }

    std::vector<int> int_vector;
};

int main () {
    constexpr unsigned INT_VECTOR_SIZE{3U};

    std::unique_ptr<MyObject> my_object_up{buildObjectWithSize<MyObject>(INT_VECTOR_SIZE)};

    (*my_object_up.get())[1] = 5;
    std::cout << (*my_object_up.get())[1] << '\n';

    MyObject &my_object_ref = *my_object_up.get();
    my_object_ref[2] = 3;
    std::cout << my_object_ref[2] << '\n';
}

      

-1


source







All Articles