Lazy shared pointer - assignment operator - c++

I have created simple lazy shared pointer class. However, currently I can have only single instance of it and my design does not support copy assignment.
/// <summary>
/// Simple lazy shared pointer
/// Pointer is initialized when first needed
///
/// Create new instance with CreateLazy static method
///
/// Copy is disabled, pointer can only be moved
/// If we would copy it not initialized
/// then two instances can be created
/// - from original and from copy
/// </summary>
template <class T>
class LazySharedPtr{
public:
static LazySharedPtr<T> Create(){
std::function<std::shared_ptr<T>()> customInit = [](){
return std::make_shared<T>();
};
return LazySharedPtr(customInit);
};
template <typename ... Args>
static LazySharedPtr<T> Create(Args ... args){
return LazySharedPtr(std::forward<Args>(args) ...);
};
LazySharedPtr() :
init(nullptr),
ptr(nullptr){
};
LazySharedPtr(std::function<std::shared_ptr<T>()> customInit) :
init(customInit),
ptr(nullptr){
};
template <typename Y>
LazySharedPtr(LazySharedPtr<Y> && other) :
init(other.init),
ptr(other.ptr){
other.init = nullptr;
other.ptr = nullptr;
};
LazySharedPtr(const LazySharedPtr& other) = delete;
virtual ~LazySharedPtr() = default;
T* operator->(){
return InitAndGet().get();
}
const T* operator->() const{
return InitAndGet().get();
}
T* operator*(){
return InitAndGet().get();
}
const T* operator*() const{
return InitAndGet().get();
}
explicit operator bool() const noexcept{
return (ptr != nullptr);
}
explicit operator std::shared_ptr<T>() const{
return InitAndGet();
}
template <typename U>
friend class LazySharedPtr;
protected:
std::function<std::shared_ptr<T>()> init;
mutable std::shared_ptr<T> ptr;
template <typename ... Args>
LazySharedPtr(Args ... args) :
init([args = std::make_tuple(std::forward<Args>(args) ...)]() mutable {
return std::apply(std::make_shared<T, Args...>, std::move(args));
}),
ptr(nullptr){
};
std::shared_ptr<T>& InitAndGet() const {
if (!ptr) { ptr = init(); }
return ptr;
}
};
Do you have any idea, how to improve this to support copy assignment?
Current design does not support this:
class MyObject { };
LazySharedPtr<MyObject> t1 = LazySharedPtr<MyObject>::Create();
LazySharedPtr<MyObject> t2 = t1;
because after initialization of t2, t1 wont be inited.
I have thought to have internal shared_ptr as pointer to pointer and pass it around. However, with raw pointer I have to manage reference count and doing std::shared_ptr<std::shared_ptr<T>> seems weird. Or does not?
Do you have any other idea?

Here's a sketch - not tested, with missing pieces that should be easy to fill in. I hope the general idea is clear.
template <class T>
class LazySharedPtr {
struct ControlBlock {
std::shared_ptr<T> ptr;
std::function<std::shared_ptr<T>()> factory;
std::shared_ptr<T> InitAndGet() {
// Add thread safety here.
if (!ptr) {
ptr = factory();
factory = nullptr;
}
return ptr;
}
};
std::function<std::shared_ptr<T>()> init;
// This member is not strictly necessary, it's just a cache.
// An alternative would be to call `init` every time.
std::shared_ptr<T> ptr;
public:
// For exposition, assume all `T`s are constructible from `int`
LazySharedPtr(int x) {
auto control = std::make_shared<ControlBlock>();
control->factory = [x]() { return std::make_shared<T>(x); };
init = [control]() {return control->InitAndGet(); }
}
template <typename U>
LazySharedPtr(const LazySharedPtr<U>& other)
: ptr(other.ptr) {
if (!ptr) {
auto other_init = other.init;
init = [other_init]() { return std::shared_ptr<T>(other_init()); };
}
}
std::shared_ptr<T> InitAndGet() {
if (!ptr) {
ptr = init();
init = nullptr;
}
return ptr;
}
};
Basically, type erasure all the way down.

Related

c++ 11 segmentation fault (core dumped),but c++ 17 not

class all {
public:
template <typename T>
all(T&& t)
: data_ptr(new derived<T>(std::forward<T>(t))) {
}
all()
: data_ptr(nullptr) {}
all(const all& o)
: data_ptr(o.clone()) {}
all(const all&& o)
: data_ptr(o.clone()) {}
all& operator=(const all& o) {
auto n = o.clone();
if (data_ptr != nullptr) {
delete data_ptr;
}
data_ptr = n;
return *this;
}
all& operator=(all&& a)
{
if (data_ptr == a.data_ptr)
return *this;
swap(data_ptr, a.data_ptr);
return *this;
}
~all() {
if (data_ptr != nullptr) {
delete data_ptr;
}
}
template <typename U>
bool is() const {
auto ret = dynamic_cast<derived<U>*>(data_ptr);
return ret != nullptr;
}
template<typename U> U& as(){
auto ret = dynamic_cast<derived<U>*>(data_ptr);
if (ret==nullptr){
throw std::runtime_error("type dynamic_cast error");
}
return ret->value;
}
template<typename U>operator U(){
return as<U>();
}
private:
struct base {
virtual base* clone() = 0;
virtual ~base(){};
};
template <typename T>
struct derived : public base {
template <typename U>
derived(U&& v)
: value(std::forward<U>(v)) {
}
T value;
base* clone() {
return new derived<T>(value);
}
};
base* data_ptr;
base* clone() const {
if (data_ptr == nullptr) {
return nullptr;
} else {
return data_ptr->clone();
}
}
};
cmake compile options:
set (CMAKE_CXX_STANDARD 11)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GLIBCXX_USE_CXX11_ABI=0 ")
test cases as follows:
int main(int argc, char const* argv[]) {
all at = 12;
cout << at.is<int>() << endl;
cout<<at.as<int>()<<endl;
cout<<int(at)<<endl;
return 0;
}
c++11,17 both compile successful,but c++ 11 throw segmentation fault when run ,c++17 run successful,why? i'm so frustrated,can someone help me?
Here is Complete Reproducible Example (not minimal).
all at = 12;
It seems that the conversion sequence is this:
A temporary object is constructed from the right hand expression, using the constructor all::all<int>(int&&).
That temporary object is used to initialise at.
Since the temporary all is non-const, the template constructor is a better match than all(const all&&).
Hence, all::all<all>(all&&) is invoked. This initialises a all::derived<all>::derived which has an all member that is initialised using all::all<all>(all&&) (because that is again, a better match than all::all(const all&&)), which invokes all::all(const all&&) which initialises all::derived<all>::derived which invokes all::all(const all&&) ... can you spot the pattern? The recursion never ends. You eventually get a stack overflow.
In C++17 at is initialised directly from the initialiser without involving a temporary object, so there is no stack overflow.
A simple fix is to add a move constructor:
all(all&& o) : data_ptr{std::exchange(o.data_ptr, nullptr)} {}
P.S. Avoid the use of bare owning pointers such as data_ptr. Use smart pointers such as std::unique_ptr instead.

Why can't I have std::optional<T> where T is abstract?

This does not work:
struct Type {
virtual bool func(const std::string& val) const noexcept = 0;
}
// in main
optional<Type> = some_function_returning_optional_type();
and fails with a error message:
error: cannot declare field 'std::experimental::fundamentals_v1::_Optional_base<Type, false>::<anonymous union>::_M_payload' to be of abstract type 'Type'
Changing the Type to have a non-pure function works, but is not appropriate in this case, because there cannot be an instance of Type in my code, only classes which inherit from it should be able to exist.
std::optional<T> stores its value in-place - it therefore needs to know the size of T to work correctly, and T must be a concrete type that can be instantiated. You can think of std::optional<T> as:
template <typename T>
struct optional
{
std::aligned_storage_t<sizeof(T), alignof(T)> _data;
bool _set;
};
An abstract type represents an interface - polymorphism and some sort of indirection are required to work with abstract types. std::optional doesn't have any indirection by design.
Your proposal of optional will of course work but it would offend me to have to write
x.value()->do_something();
and I'd be concerned that users might do something daft:
x.value().reset(); // now what?
We can achieve polymorphism with a non-polymorphic interface by using a wrapper.
Here's one way:
#include <optional>
#include <iostream>
// the Foo interface/base class
struct Foo
{
virtual ~Foo() = default;
virtual Foo* clone() const { return new Foo(*this); }
virtual void do_something() {
std::cout << "something Fooey\n";
}
};
// a service for managing Foo and classes derived from Foo
struct FooService
{
template<class Arg>
Foo* clone(Arg&& arg)
{
using d_type = std::decay_t<Arg>;
return new d_type(std::forward<Arg>(arg));
}
template<class Arg>
Foo* clone(Foo* arg)
{
return arg->clone();
}
Foo* release(Foo*& other) noexcept
{
auto tmp = other;
other = nullptr;
return tmp;
}
};
// implement the Foo interface in terms of a pimpl
template<class Holder>
struct BasicFoo
{
decltype(auto) do_something() {
return get().do_something();
}
private:
Foo& get() noexcept { return static_cast<Holder*>(this)->get_impl(); }
Foo const& get() const noexcept { return static_cast<Holder const*>(this)->get_impl(); }
};
// a type for holding anything derived from a Foo
// can be initialised by anything Foo-like and handles copy/move correctly
struct FooHolder : BasicFoo<FooHolder>
{
template
<
class Arg,
std::enable_if_t
<
std::is_base_of_v<Foo, std::decay_t<Arg>>
>* = nullptr
>
FooHolder(Arg&& arg)
: service_()
, ptr_(service_.clone(std::forward<Arg>(arg)))
{}
FooHolder(FooHolder const& other)
: service_()
, ptr_(other.ptr_->clone())
{
}
FooHolder(FooHolder && other) noexcept
: service_()
, ptr_(service_.release(other.ptr_))
{
}
FooHolder& operator=(FooHolder const& other)
{
auto tmp = other;
std::swap(ptr_, tmp.ptr_);
return *this;
}
FooHolder& operator=(FooHolder && other) noexcept
{
auto tmp = std::move(other);
std::swap(ptr_, tmp.ptr_);
return *this;
}
~FooHolder()
{
delete ptr_;
}
Foo& get_impl() noexcept { return *ptr_; }
Foo const& get_impl() const noexcept { return *ptr_; }
FooService service_;
Foo* ptr_;
};
// now we can supply as many overrides of Foo as we like
struct Bar : Foo
{
virtual Foo* clone() const { return FooService().clone(*this); }
virtual void do_something() {
std::cout << "something Barey\n";
}
};
int main()
{
std::optional<FooHolder> opt;
// note that we're initialising cleanly
opt = Bar {};
// and we don't expose the pointer so the user can't
// destroy the pimpl accidentally
opt.value().do_something();
}

What is the reason of QVector's requirement for default constructor?

I can see that that classes are treated as complex objects which are required for calling default constructor:
void QVector<T>::defaultConstruct(T *from, T *to)
{
if (QTypeInfo<T>::isComplex) {
while (from != to) {
new (from++) T();
}
...
}
But it's not clear why is it needed to construct objects in the 'hidden' area of QVector. I mean these objects are not accessible at all, so why not just to reserve the memory instead of the real object creation?
And as a bonus question, I would like to ask, if I want to have an array of non-default-constractible objects, can I safely replace QVector<T> with QVector<Wrapper<T>? where Wrapper is something like that:
class Wrapper {
public:
union {
T object;
bool hack;
};
Wrapper() {}
Wrapper(const T &t) : object { t } {}
Wrapper(const Wrapper &t) : object { t.object } {}
Wrapper &operator=(const Wrapper &value) {
object = value.object;
return *this;
}
~Wrapper() {}
};
It's easy enough to make the QVector work for a non-default-constructible type T:
#define QVECTOR_NON_DEFAULT_CONSTRUCTIBLE(Type) \
template <> QVector<Type>::QVector(int) = delete; \
template <> void QVector<Type>::resize(int newSize) { \
Q_ASSERT(newSize <= size()); \
detach(); \
} \
template <> void QVector<Type>::defaultConstruct(Type*, Type*) { Q_ASSERT(false); }
The macro needs to be present right after MyType declaration - in the header file (if any), and it must be in namespace or global scope:
struct MyType { ... };
QVECTOR_NON_DEFAULT_CONSTRUCTIBLE(MyType)
struct A {
struct MyType2 { ... };
};
QVECTOR_NON_DEFAULT_CONSTRUCTIBLE(A::MyType2);
No, the wrapper is not correct. It doesn't destruct the object member. It also doesn't offer move semantics, doesn't protect from being default-constructed, etc. The hack union member is not necessary. Nothing in a union will be default-constructed for you.
Here's a more correct wrapper - it pretty much resembles std::optional. See here to see how much nuance an optional needs :)
// https://github.com/KubaO/stackoverflown/tree/master/questions/vector-nodefault-33380402
template <typename T> class Wrapper final {
union {
T object;
};
bool no_object = false;
void cond_destruct() {
if (!no_object)
object.~T();
no_object = true;
}
public:
Wrapper() : no_object(true) {}
Wrapper(const Wrapper &o) : no_object(o.no_object) {
if (!no_object)
new (&object) T(o.object);
}
Wrapper(Wrapper &&o) : no_object(o.no_object) {
if (!no_object)
new (&object) T(std::move(o.object));
}
Wrapper(const T &o) : object(o) {}
Wrapper(T &&o) : object(std::move(o)) {}
template <class...Args> Wrapper(Args...args) : object(std::forward<Args>(args)...) {}
template <class U, class...Args> Wrapper(std::initializer_list<U> init, Args...args) :
object(init, std::forward<Args>(args)...) {}
operator T& () & { assert(!no_object); return object; }
operator T&& () && { assert(!no_object); return std::move(object); }
operator T const&() const& { assert(!no_object); return object; }
Wrapper &operator=(const Wrapper &o) & {
if (no_object)
::new (&object) T(o);
else
object = o.object;
no_object = false;
return *this;
}
Wrapper &operator=(Wrapper &&o) & {
if (no_object)
::new (&object) T(std::move(o.object));
else
object = std::move(o.object);
no_object = false;
return *this;
}
template<class... Args> T &emplace(Args&&... args) {
cond_destruct();
::new (&object) T(std::forward<Args>(args)...);
no_object = false;
return object;
}
~Wrapper() {
cond_destruct();
}
};
Since the assignment operators are ref-qualified, it disallows assigning to rvalues, so it has the IMHO positive property that the following won't compile:
Wrapper<int>() = 1 // likely Wrapper<int>() == 1 was intended

How to return different types from same function?

For study purpose, I make own Any type. I don't understand, how to return Type or Type * depending on the conditions. This is my sketch of class:
class any
{
public:
template<class T>
any(T & d)
{
data_container = new container_impl<T>(d);
}
template<class T>
any(T* d)
{
is_pointer = true;
data_container = new container_impl<T>(d);
}
bool check_is_pointer() const;
template<class T>
T a_cast(size_t id) const
{
auto real_id = data_container->get_id();
if (real_id != id)
{
//throw new exeption
}
return static_cast<container_impl<T>&>(*data_container).get_data();
}
private:
class abstract_container
{
public:
virtual ~abstract_container() { }
virtual size_t get_id() const = 0;
};
template<typename T>
class container_impl : public abstract_container
{
public:
container_impl(T const& value)
{
data = value;
id = type_id<T>();
pointer_data = nullptr;
}
container_impl(T const* value)
{
pointer_data = value;
id = type_id<T*>();
}
T get_data()
{
return data;
}
size_t get_id() const override
{
return id;
}
private:
T data;
T const* pointer_data;
size_t id;
};
abstract_container *data_container = nullptr;
bool is_pointer = false;
};
template<class T>
T any_cast(any const& obj)
{
size_t id = type_id<T>();
return obj.a_cast<T>(id);
}
template<class T>
T* any_cast(any const& obj)
{
size_t id = type_id<T>();
return obj.a_cast<T>(id);
}
As you can see, I would like to create two functions any_cast, which return different types.
You can't return different types. The way that Boost does it is actually with different signatures - note that the two different versions take different arguments. One takes a reference and the other takes a pointer.
template <typename T> // this one will try to cast your any down to T
T any_cast(any& ); // and return it if that succeeds, else throw
template <typename T> // this one will NOT throw, but will return
T* any_cast(any* ); // a pointer instead
Example usage of the two different functions:
any a = 5;
any_cast<double>(a); // throws
double* v = any_cast<double>(&a); // won't throw
assert(v == nullptr);
int* vi = any_cast<int>(&a); // succeeds
assert(*vi == 5);

Calling function with different argument C++

This question is just for improvement purpose. There is a function below:
void Func_A(u8* arg) {
bool local_arg=false;
if(!arg) {
//allocate memory for arg
local_arg=true;
}
//process arg
I am calling this function from multiple places with NULL and non NULL argument.
I just want to ask whther there is a better way of handling such this without local_arg or not.
You can sometimes use...
u8* p = arg ? arg : new u8(...);
...do things with *p...
if (!arg) delete p;
A little fancier, you can write a smart pointer with runtime-configurable ownership:
template <typename T>
class Uncertain_Ownership_Ptr
{
public:
enum Ownership { Own_It, Dont_Own_It };
Uncertain_Ownership_Ptr(T* p, Ownership own) : p_(p), own_(own) { }
Uncertain_Ownership_Ptr(const Uncertain_Ownership_Ptr&) = delete;
void operator=(const Uncertain_Ownership_Ptr&) = delete;
~Uncertain_Ownership_Ptr() { if (own_ == Own_It) delete p_; }
T& operator*() { return *p_; }
const T& operator*() const { return *p_; }
T* operator->() { return p_; }
const T* operator->() const { return p_; }
private:
T* p_;
Ownership own_;
};
...then...
void Func_A(u8* arg)
{
Uncertain_Ownership_Ptr p(arg ? arg : new u8(...),
arg ? Uncertain_Ownership_Ptr::Dont_Own_It : Uncertain_Ownership_Ptr::Own_It);
// use *p ...
}