C++: usage of template for pointers with overloading arrow (->) operator - c++

I wrote this template class for pointers usage
(I need smart pointers, but I can't use boost or C++11):
template<class T>
class TreePointer{
public:
TreePointer(){
isRefOnly=false;
data=NULL;
};
TreePointer(T* data){
this->data=data;
this->isRefOnly=false;
}
TreePointer(const TreePointer& anotherPtr){
this->data=anotherPtr.data;
this->isRefOnly=true;
}
virtual ~TreePointer(){
if (!isRefOnly){
delete data;
}
}
T* operator->() const{
return data;
}
void operator=(const TreePointer &anotherPtr){
this->data=anotherPtr.data;
this->isRefOnly=true;
}
private:
T* data;
bool isRefOnly;
};
And I have big class with many methods, like this:
class WrittenBigClassWithManyMethods{
public:
WrittenBigClassWithManyMethods(int v){
this->v=v;
}
int sum(WrittenBigClassWithManyMethods* a){
return v+a->v;
}
int v;
};
This usage of my smart pointers work perfectly:
TreePointer<WrittenBigClassWithManyMethods> tp(new WrittenBigClassWithManyMethods(5));
WrittenBigClassWithManyMethods* simpleClass=new WrittenBigClassWithManyMethods(5);
cout << tp->sum(simpleClass);
But it usage isn't work:
TreePointer<WrittenBigClassWithManyMethods> tp2(new WrittenBigClassWithManyMethods(5));
cout << tp->sum(tp2);
How i can change my template for pointers to make invoking methrod sum of class WrittenBigClassWithManyMethods with parameter of type TreePointer, without any changes for class WrittenBigClassWithManyMethods and its any usages? If this is not possible, how I minimize the changes for class WrittenBigClassWithManyMethods and its usage?

Usually you'll want to overload the unary operator * (de-reference) too, returning a T&. Then you can have both a reference and the original pointer by taking the address of the result:
tp1->method_that_takes_ref(*tp2); // With operator*()
tp1->method_that_takes_ptr(&*tp2); // Works, but syntax might be a bit surprising
Another way to get at the pointer inside would be to call operator -> directly, but that would be a bit awkward. You are likely better off providing some kind of "get" method, like the one in unique_ptr, that simply returns the raw pointer:
tp1->method_that_takes_ptr(tp2.operator->()); // Works, but ugh
tp1->method_that_takes_ptr(tp2.get()); // Much clearer

Add a conversion operator to T*:
operator T*() {
return data;
}
Now the compiler will call it whenever it wants to convert a TreePointer<SomeClass> to a SomeClass*.

Related

Implementing a custom allocator with fancy pointers

I'm trying to implement my own allocator, which should work with STL containers and use a custom fancy pointer implementation.
I'm pretty sure, that my classes fulfill all requirements (according to cppreference) but my implementation doesn't compile for std::list because there is no conversion from my fancy pointer to a normal pointer.
A minimal example which shows the problem, (but is obviously not my real implementation):
fancy_ptr.h:
#include <cstddef>
template<typename T>
class FancyPtr {
T *ptr;
FancyPtr(T *ptr, bool) : ptr(ptr) {}; //Bool to be non standart
public:
using element_type = T;
FancyPtr(std::nullptr_t n) : FancyPtr() {};
template<class S>
operator FancyPtr<S>() {
return {ptr};
}
T &operator*() { return *ptr; }
T &operator[](size_t n) { return ptr[n]; }
T *operator->() { return ptr; }
bool operator==(const FancyPtr &other) { return ptr == other.ptr; };
static FancyPtr pointer_to(element_type &r) { return FancyPtr(&r, false); };
};
TrivialAllocator.h:
#include "fancy_ptr.h"
template<typename T>
class TrivialAllocator {
public:
using pointer = FancyPtr<T>;
using value_type = T;
TrivialAllocator() = default;
template<typename Other>
TrivialAllocator(const TrivialAllocator<Other> &other) {};
template<typename Other>
TrivialAllocator(TrivialAllocator<Other> &&other) {};
TrivialAllocator(TrivialAllocator &alloc) = default;
pointer allocate(size_t n) { return pointer::pointer_to(*new T[n]); }
void deallocate(pointer ptr, size_t n) { delete[] &*ptr; };
bool operator==(const TrivialAllocator &rhs) const { return true; };
bool operator!=(const TrivialAllocator &rhs) const { return false; };
};
main.cpp:
#include "TrivialAllocator.h"
#include <list>
int main() {
struct Test {};
using AllocT = std::allocator_traits<TrivialAllocator<long double>>;
static_assert(std::is_same_v<FancyPtr<long double>,std::pointer_traits<AllocT::pointer>::pointer>);
static_assert(std::is_same_v<FancyPtr<Test>, std::pointer_traits<AllocT::pointer>::rebind<Test>>);
std::list<long double, AllocT::allocator_type> list;
}
The static assertions are ok.
Can anybody tell me what I have to to to get this working?
PS: I know that operator-> is in something like a conversion operator, but the underlying problem is that std::list seem not to save my fancy pointers, but raw pointers.
Your class does not fulfil all the requirements.
Your pointer type must be a Cpp17RandomAccessIterator (operator++, operator+=, operator+, operator--, operator-, operator-=, operator!=, operator<, operator>=, operator<=)
Your FancyPtr<void> (which would be std::allocator_traits<TrivialAllocator<T>>::void_pointer) does not compile because of the operator[] and pointer_to(void&) (void_pointer and const_void_pointer don't need to be random access iterators)
You can't convert from a pointer to a void_pointer (You need to change your conversion operator to return {static_cast<S*>(ptr);}, or have the static_cast on a constructor of void_pointer)
You can't convert from your pointer type to bool which is one of the requirements for NullablePointer.
Your allocators's allocate and deallocate shouldn't call constructors or destructors.
However, even after making it a valid pointer type for an allocator, you still run into issues with how libstdc++ and libc++ deals with pointers. At some points in the code, there is a cast from FancyPtr<ListNode> to FancyPtr<ListNodeBase>, where ListNode derives from ListNodeBase. This is not part of the requirements of a pointer type, but is used by the implementation of std::list in those standard libraries anyways. You allow this by having operator FancyPtr<T> for any T, which is used. The standard libraries may be able to fix this by converting to a void pointer then to the base class, if the node classes are standard layout.
libstdc++ also internally uses raw T* pointers everywhere, so there are points where it implicitly tries to convert from T* to FancyPtr<T>. Unfortunately, the only way to support this would be to have a public FancyPtr(T*) constructor and a conversion to a raw pointer operator T*(). libstdc++ could fix this without breaking ABI by using p ? std::pointer_traits<pointer>::pointer_to(*p) : pointer(nullptr) to convert a T* p to a fancy pointer, and std::to_address for the reverse.
Microsoft's STL's std::list has no problems with a valid pointer type.
Here is a example implementation of a fancy pointer type that fulfils the requirements and has the workarounds for the 2 standard library implementations mentioned here: https://godbolt.org/z/vq9cvW
After some digging into the problem, I guess this is simply impossible due to libstdc++ internal limitations. This is a known old bug, "Node-based containers don't use allocator's pointer type internally":
Container nodes are linked together by built-in pointers, but they should use the allocator's pointer type instead. Currently I think only std::vector does this correctly. ...
It should work with Clang and libc++ (use -stdlib=libc++ command line option) with a couple of fixes:
FancyPtr<void>::pointer_to(...) should be a valid member function. Now it is not, because void& does not exist.
FancyPtr should provide operator!=(...) member function.
These fixes are needed to make your code at least compilable. Whether it will work correctly is out of scope of this answer.

How to pass data to a templated collection

I have to write a generic data structure that resembles a C++ vector as part of an assignment.
This is my idea for the Vector:
template<typename T>
class MyVector {
private:
T* data_;
size_t size_;
public:
MyVector();
MyVector(const MyVector &otherVector);
// which one should I use?
add(const T& value);
add(T value);
~MyVector();
};
Now I wonder how to pass values to the methods. Coming from Java I am a bit overwhelmed. In Java you wouldn't hesitate and pass the value by reference, the GC would never delete the object if it is still referenced.
In C++ you would create a mess if you would pass by reference considering code like this:
void myFunction(MyVector &myVector) {
int a = 5;
myVector.add(a);
}
int main() {
auto vector = MyVector<int>();
myFunction(vector);
// now the vector contains a reference to
// something that doesn't exist anymore.
}
How do you solve this problem? Would you just pass by reference and create a copy or do you pass by value (which creates a copy for you)
Looking at the C++ std::vector interface I see that they use references.
I just don't see the value of passing by reference if you have to create your own copy.
add(const T& value) is ok, you just should be sure that there is properly defined assign operator for T. So, the implementation will be:
void Add(const T& value) {
if (m_size == m_maxSize) realloc(); // stuff to have enough space
m_data[m_size++] = value; // here copy is creating
}
default impl of assign operator just byte-copy fields of class, it is not always correct.
Other solution, if you want more java-style semantic, is to make T = shared_ptr<YourType> or T = YourType*
The latter is rather difficult because require skill of manual lifetime control, so is undesirable for c++ beginners.
void myFunction(MyVector<shared_ptr<X>> & myVector)
{
shared_ptr<X> x(new X(...));
myVector.add(x);
}
works similar to references in Java.
Other way, that was used in old times:
template<typename T>
class MyVector {
private:
T** data_; // now you have array of pointers, so should be careful
....
add(T* value);
....
}
void myFunction(MyVector<X> & myVector)
{
X * x = new X(...);
myVector.add(x); // now x belongs to myVector and it should handle its lifetime
}

does C++ have a self initializing pointer

I am a bit embarrassed of asking such a simple question:
Is there any pointer class in cpp that initializes itself with nullptr but is 100% compatible to a basic c-stylish pointer?
to write:
extern "C" void someFunction(const Struct* i_s);
std::ptr<Struct> p;
// ...
p = new Struct;
// ...
someFunction(p);
Is there such a thing?
Or maybe in boost or Qt?
Edit: to make it clear: iam not searching for a smart pointer that takes ownership of the pointer and does ref counting.
You can use the following syntax
std::unique_ptr<Struct> up{};
(or std::shared_ptr). This way, the pointer is value-initialized, i.e. nullptr is being assigned to it.
See http://en.cppreference.com/w/cpp/memory/unique_ptr/unique_ptr for details about the default constructor.
If you looking for a "smart" pointer that just initialized by default with nullptr, then you can write a wrapper. A very basic version below:
#include <iostream>
template <typename T>
struct safe_ptr
{
T* _ptr;
explicit safe_ptr(T* ptr = nullptr):_ptr{ptr}{}
operator T*() const {return _ptr;}
safe_ptr& operator=(T* rhs)
{
_ptr = rhs;
return *this;
}
};
void test(int* p){}
int main()
{
safe_ptr<int> s;
if(s==nullptr)
std::cout << "Yes, we are safe!" << std::endl;
// test that it "decays"
test(s);
s = new int[10]; // can assign
delete[] s; // can delete
}
There is no such thing in C++ since all of the special pointer classes implement some form of ownership other than "maintained by someone else". You could technically use shared_ptr with an empty deleter but that adds reference counting you don't actually need.
The correct C++ solution is to just always add = 0; or = nullptr; to your raw pointer declarations that aren't initialized at declaration.
All that said, this question is tagged just as C++ so the idiomatic answer is to not use raw pointers in your code (except for non-owning cases obviously).
100% compatible to a basic c-stylish pointer
std::unique_ptr and std::shared_ptr do not have automatic conversions to a raw pointer, and that's a good thing as it would inevitably lead to horrible bugs. They take ownership, and in your comments you explicitly say:
the pointer should not take ownership of the given Pointer.
If you insist, you can define a "smart" pointer class yourself:
template <class T>
class RawPointer final
{
private:
T* raw_ptr;
public:
RawPointer(T* raw_tr) : raw_ptr(raw_ptr) {}
RawPointer() : raw_ptr(nullptr) {}
operator T*() const { return raw_ptr; }
};
struct Struct
{
};
void someFunction(const Struct* i_s);
int main()
{
RawPointer<Struct> p;
someFunction(p);
}
Is this a good idea? Probably not. You should just get into the habit of initializing your raw pointers:
Struct* p = nullptr;
On the other hand, people are thinking about a very similar addition to the standard library in the future. You may find A Proposal for the World’s Dumbest Smart Pointer an interesting read.
If this is really the behavior that you want, it would be trivial to implement it yourself in a template. Here's one such implementation:
template<class T>
class ptr_t{
T* ptr;
public:
ptr_t() : ptr(nullptr){ }
ptr_t(const ptr_t& other) : ptr(other.ptr){ }
ptr_t(T* other) : ptr(other){ }
T& operator*(){
return *ptr;
}
T* operator->(){
return ptr;
}
template<class U>
operator U(){
return (U)ptr;
}
}
However, the amount of convenience you will gain from such a device will be rather limited. You're probably much better off taking another approach.

How can I define a function that takes as a parameter a pointer value of any kind?

I'm implementing for practice a smart pointer class.
I already defined an assignment operator overload that takes another instance of the same class. Now I want to define an overload of this operator, that takes any pointer. So I should be able to do stuff like smartPointer = &someObject; or smartPointer = NULL;, etc.
How can I go about doing that? Should I pass in a void*? Something else?
As a more general question (and I know this is rarely desired): what kind of parameter tells the compiler that any pointer can be passed in?
You can use a template function to make your object allow any pointer to be assigned to it.
template<typename T>
void operator=(T* obj)
{
//Your code here
}
However, its not a smart pointer if you could assign it any raw pointers as it could be assigned to more than one smart pointer object and then there would be a problem while deleting the pointer.
Following may help:
class MySharedPointer
{
public:
template <typename T>
MySharedPointer& operator = (T* p)
{
ptr.reset(p, [](void* p) { delete static_cast<T*>(p); });
return *this;
}
// nullptr is not a pointer, so it should have its own overload.
MySharedPointer& operator = (std::nullptr_t) {
ptr.reset();
return *this;
}
private:
std::shared_ptr<void> ptr;
};
Live example

Initializing a std::unique_ptr by passing the address of the pointer

I am creating a class which interops with some Windows API code, now one of the pointers I have to initialize is done by calling a native function which initializes it.
My pointers are of type std::unique_ptr with a custom deleter, which calls the WinAPI deleter function provided, however I cannot pass the unique_ptr with the & address-of operator to the init-function. Why?
I have created a sample that demonstrates my problem:
#include <memory>
struct foo
{
int x;
};
struct custom_deleter {};
void init_foo(foo** init)
{
*init = new foo();
}
int main()
{
std::unique_ptr<foo, custom_deleter> foo_ptr;
init_foo(&foo_ptr);
}
The compiler barks and says:
source.cpp: In function 'int main()':
source.cpp:19:21: error: cannot convert 'std::unique_ptr<foo, custom_deleter>*' to 'foo**' for argument '1' to 'void init_foo(foo**)'
Somewhere under the covers, unique_ptr<foo> has a data member of type foo*.
However, it's not legitimate for a user of the class to directly modify that data member. Doing so would not necessarily preserve the class invariants of unique_ptr, in particular it wouldn't free the old pointer value (if any). In your special case you don't need that to happen, because the previous value is 0, but in general it should happen.
For that reason unique_ptr doesn't provide access to the data member, only to a copy of its value (via get() and operator->). You can't get a foo** out of your unique_ptr.
You could instead write:
foo *tmp;
init_foo(&tmp);
std::unique_ptr<foo, custom_deleter> foo_ptr(tmp);
This is exception-safe for the same reason that std::unique_ptr<foo, custom_deleter> foo_ptr(new foo()); is exception-safe: unique_ptr guarantees that whatever you pass in to its constructor will eventually get deleted using the deleter.
Btw, doesn't custom_deleter need an operator()(foo*)? Or have I missed something?
Steve has already explained what the technical problem is, however, the underlying problem goes much deeper: The code employs an idiom helpful when you deal with naked pointers. Why does this code do two-step initialization (first create the object, then initialize it) in the first place? Since you want to use smart pointers, I'd suggest you carefully adapt the code:
foo* init_foo()
{
return new foo();
}
int main()
{
std::unique_ptr<foo, custom_deleter> foo_ptr( init_foo() );
}
Of course, renaming init_foo() to create_foo() and having it return a std::unique_ptr<foo> directly would be better. Also, when you use two-step initialization, it's often advisable to consider using a class to wrap the data.
You can use the following trick:
template<class T>
class ptr_setter
{
public:
ptr_setter(T& Ptr): m_Ptr{Ptr} {}
~ptr_setter() { m_Ptr.reset(m_RawPtr); }
ptr_setter(const ptr_setter&) = delete;
ptr_setter& operator=(const ptr_setter&) = delete;
auto operator&() { return &m_RawPtr; }
private:
T& m_Ptr;
typename T::pointer m_RawPtr{};
};
// Macro will not be needed with C++17 class template deduction.
// If you dislike macros (as all normal people should)
// it's possible to replace it with a helper function,
// although this would make the code a little more complex.
#define ptr_setter(ptr) ptr_setter<decltype(ptr)>(ptr)
and then:
std::unique_ptr<foo, custom_deleter> foo_ptr;
init_foo(&ptr_setter(foo_ptr));
I eventually came up with an approach that allows to initialise unique_ptr's with a code like this:
struct TOpenSSLDeleter { ... }; // Your custom deleter
std::unique_ptr<EVP_MD_CTX, TOpenSSLDeleter> Ctx;
...
Ctx = MakeUnique(EVP_MD_CTX_create()); // MakeUnique() accepts raw pointer
And here is the solution:
template <class X>
struct TUniquePtrInitHelper {
TUniquePtrInitHelper(X *Raw) noexcept {
m_Raw = Raw;
}
template <class T, class D>
operator std::unique_ptr<T, D>() const noexcept {
return std::unique_ptr<T, D>(m_Raw);
}
private:
X *m_Raw;
};
template <class X>
TUniquePtrInitHelper<X> MakeUnique(X *Raw) noexcept {
return {Raw};
}