I am trying to write my own Allocator which can be used in STL. That far I could almost successfully finish but with one function I have my problem:
The Allocator used in STL should provide the function construct [example from a standard allocator using new & delete]:
// initialize elements of allocated storage p with value value
void construct (T* p, const T& value)
{
::new((void*)p)T(value);
}
I am stuck how to rewrite this using my own function which replaces the new keyword initializing it with the value.
This function construct is for example used in this code: fileLines.push_back( fileLine );
where
MyVector<MyString> fileLines;
MyString fileLine;
These are my typedefs where I use my own Allocator:
template <typename T> using MyVector = std::vector<T, Allocator<T>>;
using MyString = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
I am confused because here is allocated a pointer to T which can be for example [when I understood it correctly] MySstring.
Do I understand it correctly that the pointer - allocated by new - will have 10 bytes, when value is 123456789 and then the provided value is copied to the new pointer?
My question:
How to rewrite the one line of code using my own function? For me the difficult point is how to get the length of value [which can have any type] in order I can correctly determinate the length of the allocated block and how to copy it in order it works for all possible types T?
The new operator in the construct function does not allocate anything at all, it's a placement new call, which takes an already allocated chunk of memory (which needs to have been previously allocated some way, and at least as large as sizeof(T)) and initializes it as a T object by calling the constructor of T, pretending that the memory pointed to by p is a T object.
::new int(7) calls the default new operator, gets some memory big enough for an int, and constructs an int with the value 7 in it.
::new(ptr) int(7) takes a void* called ptr, and constructs an int with the value 7 in it. This is called "placement new".
The important part is what is missing from the second paragraph. It does not create space, but rather constructs an object in some existing space.
It is like saying ptr->T::T() ptr->int::int(7) where we "call the constructorofinton the memory location ofptr, exceptptr->T::T()orptr->int::int(7)are not valid syntaxes. Placementnew` is the way to explicitly call a constructor in C++.
Similarly, ptr->~T() or ptr->~int() will call the destructor on the object located at ptr (however, there is no ~int so that is an error, unless int is a template or dependent type, in which case it is a pseudo-destructor and the call it ignored instead of generating an error).
It is very rare that you want to change construct from the default implementation in an allocator. You might do this if your allocator wants to track creation of objects and attach the information about their arguments, without intrusively modifying the constructor. But this is a corner case.
Related
I can't understand the function move in c++11.
From here, I got things below:
Although note that -in the standard library- moving implies that the
moved-from object is left in a valid but unspecified state. Which
means that, after such an operation, the value of the moved-from
object should only be destroyed or assigned a new value; accessing it
otherwise yields an unspecified value.
In my opinion, after move(), the moved-from object has been "clear". However, I've done a test below:
std::string str = "abcd";
std::move(str);
std::cout<<str;
I got abcd on my screen.
So has the str been destroyed? If so, I could get abcd because I'm just lucky? Or I misunderstood the function move?
Besides, when I read C++ Primer, I got such a code:
class Base{/* ... */};
class D: public Base{
public:
D(D&& d): Base(std::move(d)){/* use d to initialize the members of D */}
};
I'm confused now. If the function move will clear the object, the parameter d will be clear, how could we "use d to initialize the members of D"?
std::move doesn't actually do anything. It's roughly analogous to a cast expression, in that the return value is the original object, but treated differently.
More precisely, std::move returns the object in a form which is amenable to its resources being 'stolen' for some other purpose. The original object remains valid, more or less (you're only supposed to do certain special things to it, though that's primarily a matter of convention and not necessarily applicable to non-standard-library objects), but the stolen-away resources no longer belong to it, and generally won't be referenced by it any more.
But! std::move doesn't, itself, do the stealing. It just sets things up for stealing to be allowed. Since you're not doing anything with the result, let alone something which could take advantage of the opportunity, nothing gets stolen.
std::move doesn’t move anything. std::move is merely a function template that perform casts. std::move unconditionally casts its argument to an rvalue,
std::move(str);
With this expression you are just doing type cast from lvalue to rvalue.
small modification in program to understand better.
std::string str = "abcd";
std::string str1 = std::move(str);
std::cout<<str<<std::endl;
std::cout<<str1<<std::endl;
str lvalue typecast to rvalue by std::move, std::string = std::move(str); =>this expression call the string move constructor where actual stealing of resources take placed. str resources(abcd) are steeled and printed empty string.
Here is sample implementation of move function. Please note that it is not complete implementation of standard library.
template<typename T> // C++14; still in
decltype(auto) move(T&& param) // namespace std
{
using ReturnType = remove_reference_t<T>&&;
return static_cast<ReturnType>(param);
}
Applying std::move to an object tells the compiler that the object is eligible to be moved from. It cast to the rvalue.
class Base{/* ... */};
class D: public Base{
public:
D(D&& d): Base(std::move(d)){/* use d to initialize the members of D */}
};
Base(std::move(d)) it will do up-casting only move the base class part only.
Here one more interesting thing to learn for you. If you do not invoke base class destructor with std::move like D(D&& d): Base(d) then d will be considered as lvalue and copy constructor of Base class involved instead of move constructor. Refer for more detail Move constructor on derived object
Why can not I use new [ ] with smart_pointers?
Actually I can not understand this piece of text.
Caution You should use an auto_prt or shared_ptr object only for
memory allocated by new, not for memory allocated by new []. You
should not use auto_ptr, shared_ptr,orunique_ptr for memory not
allocated via new or, in the case of unique_ptr, via new or new[].
Why can not I use new[] with smart pointers?
In general you can, but that smart pointer must be aware of the fact that it stores a dynamically allocated array, not a single object. This is because objects allocated with operator new[] should be deallocated with operator delete[], not delete. How can a smart pointer know which operator should be applied?
The distinction is made by providing a specialization of smart pointer class templates for array types, like it is currently done in std::unique_ptr<T>:
std::unique_ptr<int> ptr(new int); // will call delete
std::unique_ptr<int[]> arr(new int[5]); // will call delete[]
↑↑
DEMO
However, that syntax does not (yet) apply to all smart pointer types available in the Standard Library.
For comparison, the Boost Smart Pointers library provides separate class templates for storing pointers to dynamically allocated arrays:
boost::shared_array<int> arr1(new int[5]); // will call delete[]
// ~~~~^
boost::scoped_array<int> arr2(new int[5]); // will call delete[]
// ~~~~^
DEMO 2
You should use an auto_ptr or shared_ptr object only for memory allocated by new, not for memory allocated by new [].
std::auto_ptr<T>(† 2017)1 applies a plain delete operator to a pointer it stores, and there is no way to change that behavior. As such, storing a pointer to an array is not an option.
As far as std::shared_ptr<T> is concerned, by default it does the same (calls operator delete). To change that behavior, and properly deallocate a memory area of a stored array, you could use a custom deleter, like std::default_delete<T[]>:
std::shared_ptr<int> arr(new int[5], std::default_delete<int[]>{});
↑↑
or some other provided by yourself:
std::shared_ptr<int> arr(new int[5], [](int* ptr) { delete[] ptr; } );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
DEMO 3
However, a missing specialization for std::shared_ptr<T[]> implies no operator[] that could let you easily access the elements of a stored array, which leads to unintuitive syntax like arr.get()[0].
With proposal N4077 introduced, there will be a specialization for array type pointers:
std::shared_ptr<int[]> arr(new int[5]); // will call delete[]
↑↑
You should not use auto_ptr, shared_ptr, or unique_ptr for memory not allocated via new or, in the case of unique_ptr, via new or new[].
This excerpt simply states that one should not construct a smart pointer from a pointer to an object that was not allocated dynamically, as (by default) it would result in calling delete on something that was not allocated with new (ditto new[]/delete[]).
What is the difference between unique_ptr<double[]> p1(new double[2]);, unique_ptr<double> p2(new double[2]);, unique_ptr<double[]> p3(new double(2)); ?
std::unique_ptr<double[]> p1(new double[2]);
OK: Constructs a unique_ptr from (and takes ownership of) a pointer to an array of two doubles. It will call delete[] to deallocate the memory pointed.
std::unique_ptr<double> p2(new double[2]);
Wrong: Constructs a unique_ptr from (and takes ownership of) a pointer to an array of two doubles. It will call delete (!) to deallocate the memory pointed. (possibly undefined behavior - a mismatch between new[] and delete).
std::unique_ptr<double[]> p3(new double(2));
Wrong: Constructs a unique_ptr from (and takes ownership of) a pointer to a single double initialized to value 2. It will call delete[] (!) to deallocate the memory pointed. (possibly undefined behavior - a mismatch between new and delete[]).
1 std::auto_ptr<T> is deemed deprecated in favor of std::unique_ptr<T> since C++11, and will be removed from the Standard Library in C++1z according to N4168.
Examples:
#include <memory>
int
main()
{
auto p1 = std::unique_ptr<char[]>{new char[3]}; // C++11
auto p2 = std::shared_ptr<char>{new char[3], [](char* p) {delete [] p;}}; // C++11
auto p3 = std::make_unique<char[]>(3); // C++14
}
The first and second are good for C++11 and forward. The 3rd was introduced in C++14. The first and third represent unique ownership of the new, and the second has shared ownership of the new.
I have this declared above:
char PandaImage[] = "images/panda.png";
SDL_Texture* PandaTexture = nullptr;
I have a function to create textures:
void LoadMedia( SDL_Texture *ThisTexture, char *Image )
{
SDL_Surface* TempSurface = nullptr;
.......................
ThisTexture = SDL_CreateTextureFromSurface( gRenderer, TempSurface );
I call it as:
LoadMedia( PandaTexture, PandaImage );
It builds, logs the image loaded and texture created, but no image
If I hard change the line ( use Panda directly instead of This ):
PandaTexture = SDL_CreateTextureFromSurface( gRenderer, TempSurface );
My image is there.
I have always had trouble with & * and passing.
Is there a good, simple help for me?
Thanks for your kind help - back to Google for now
In short, I think you could solve your problem by changing the function to:
void LoadMedia( SDL_Texture** thisTexture, char* Image)
{
...
(*thisTexture) = SDL_CreateTextureFromSurface( gRenderer, TempSurface);
}
And by calling the function using:
LoadMedia( &PandaTexture, PandaImage);
An explanation:
Variables and Pointers
A variable is used to store data (a primitive or a class instance). For example:
int a = 10;
stores an integer in memory. This means, that symbol 'a' now represents number 10, which is stored somewhere in your computer's memory as 4 bytes.
A pointer is used to store an address (this address points towards a variable). For example:
int* a_address = 1234;
says that there is an integer stored at address 1234 in your computer's memory. A pointer always takes up the same amount of space (4 bytes on a 32 bit machine and 8 bytes on a 64 bit machine), as it simply stores an address.
Getting the Address of a Variable [&]
You will rarely ever set the address of a pointer yourself. Often, pointers are the result of a "new" call. Using "new" reserves memory to store an instance of the class you want to create, and returns the address of the object. In essence, it says: "I created an object for you, and you can find it at this location in your memory".
Alternatively, when you have a normal variable (primitive of class instance), you can find its address by using the & character. For example:
int a = 10;
int* a_address = &a;
says: "store the location of variable a in pointer a_address. Why would you do this? Say you have a very large instance (for example an SDL_Texture consisting of many, many pixels) and you want to pass it to a function (or pass it back outside of the function). If you were to pass it to the function as SDL_Texture thisTexture, you are copying the entire object (a so-called pass by value). This is time consuming. Alternatively, you could simply pass the address to the function, as an SDL_Texture * thisTexture. This is a so called pass by reference, and it is much faster as you can imagine.
Getting the Variable at an Address [*]
Obviously, if you have an address, you also need a way to get the actual variable at that address. This is done using the * character. It is called "dereferencing". For example:
int a = 10;
int* a_address = &a;
int b = (*a_address);
This last line says: "Give me the variable, stored at address a_address, and put it in b".
Function Parameters Going Out-of-scope
When a function ends, its local variables (including parameters) go out-of-scope. This means that their memory is freed (for variables, not for dynamically allocated objects stored as pointers!). Their values will be forgotten. In your case, you are passing an SDL_Texture * as a parameter. This means, a copy is made of the address stored in PandaTexture. This address is copied over to thisTexture. You then write the return value of SDL_CreateTextureFromSurface to thisTexture. Next the function ends, and thisTexture goes out-of-scope. As a result, the location of your SDL_Texture (the SDL_Texture * pointer) is lost forever. You actually want to store the address to pointer PandaTexture, but as you can see, the address is only written to thisTexture.
Solution: How to Fix your Function
We can fix this by passing a pointer, to your pointer called PandaTexture. A "pointer to a pointer" is written as:
SDL_Surface** thisTexture;
We want to pass the address of pointer PandaTexture to this. This way, we can write to PandaTexture from inside your method! After all, we know where PandaTexture stores its pointer in memory, allowing us to change it. To actually put the address of PandaTexture in it, we need to use the & character in the function call as such:
LoadMedia(&PandaTexture, PandaImage);
Next, inside of our function, we want to change the value of PandaTexture. However, we were passed &PandaTexture and not PandaTexture itself. To write the value of &PandaTexture (the address where our texture will be stored), we need dereferencing, as such:
(*thisTexture) = SDL_CreateTextureFromSurface(gRenderer, TempSurface);
This works because: "thisTexture is a pointer to a pointer to an SDL_Texture (aka an SDL_Texture**). By dereferencing it, we obtain a pointer to an SDL_Texture (aka an SDL_Texture*). Here we can store the return value of the SDL_CreateTextureFromSurface function.
Why do we not run into out-of-scope issues here? Parameter thisTexture will still go out of scope, and its value will be forgotten. But! We didn't write to thisTexture, instead we wrote our SDL_Texture * pointer to the address that thisTexture points to! This bit of memory is not cleared due to scoping, so we can view the results from outside the function!
In summary, you can solve your problem using a pointer to a pointer. I hope the above clears up the concepts of pointers, variables, addresses and dereferencing a bit!
Problem:
Let's assume I have an algorithm that takes a unique_ptr to some type:
void FancyAlgo(unique_ptr<SomeType>& ptr);
Now I have shared_ptr sPtr to SomeType, and I need to apply the same algorithm on sPtr. Does this mean I have to duplicate the algorithm just for the shared_ptr?
void FancyAlgo(shared_ptr<SomeType>& sPtr);
I know smart pointers come with ownership of the underlying managed object on the heap. Here in my FancyAlgo, ownership is usually not an issue. I thought about stripping off the smart pointer layer and do something like:
void FancyAlgo(SomeType& value);
and when I need to call it with unique_ptr:
FancyAlgo(*ptr);
likewise for shared_ptr.
1, Is this an acceptable style in PRODUCTION code?(I saw somewhere that in a context of smart pointers, you should NOT manipulate raw pointers in a similar way. It has the danger of introducing mysterious bugs.)
2, Can you suggest any better way (without code duplication) if 1 is not a good idea.
Thanks.
Smart pointers are about ownership. Asking for a smart pointer is asking for ownership information or control.
Asking for a non-const lvalue reference to a smart pointer is asking for permission to change the ownership status of that value.
Asking for a const lvalue reference to a smart pointer is asking for permission to query the ownership status of that value.
Asking for an rvalue reference to a smart pointer is being a "sink", and promising to take that ownership away from the caller.
Asking for a const rvalue reference is a bad idea.
If you are accessing the pointed to value, and you want it to be non-nullable, a reference to the underlying type is good.
If you want it to be nullable, a boost::optional<T&> or a T* are acceptable, as is the std::experimental "world's dumbest smart pointer" (or an equivalent hand-written one). All of these are non-owning nullable references to some variable.
In an interface, don't ask for things you don't need and won't need in the future. That makes reasoning about what the function does harder, and leads to problems like you have in the OP. A function that reseats a reference is a very different function from one that reads a value.
Now, a more interesting question based off yours is one where you want the function to reseat the smart pointer, but you want to be able to do it to both shared and unique pointer inputs. This is sort of a strange case, but I could imagine writing a type-erase-down-to-emplace type (a emplace_sink<T>).
template<class T>
using later_ctor = std::function<T*(void*)>;
template<class T, class...Args>
later_ctor<T> delayed_emplace(Args&&...args) {
// relies on C++1z lambda reference reference binding, write manually
// if that doesn't get in, or don't want to rely on it:
return [&](void* ptr)->T* {
return new T(ptr)(std::forward<Args>(args));
};
}
namespace details {
template<class T>
struct emplace_target {
virtual ~emplace_target() {}
virtual T* emplace( later_ctor<T> ctor ) = 0;
};
}
template<class T>
struct emplacer {
std::unique_ptr<emplace_target<T>> pImpl;
template<class...Args>
T* emplace( Args&&... args ) {
return pImpl->emplace( delayed_emplace<T>(std::forward<Args>(args)...) );
}
template<class D>
emplacer( std::shared_ptr<T, D>& target ):
pImpl( new details::emplace_shared_ptr<T,D>(&target) ) // TODO
{}
template<class D>
emplacer( std::unique_ptr<T, D>& target ):
pImpl( new details::emplace_unique_ptr<T,D>(&target) ) // TODO
{}
};
etc. Lots of polish needed. The idea is to type-erase construction of an object T into an arbitrary context. We might need to special case shared_ptr so we can call make_shared, as a void*->T* delayed ctor is not good enough to pull that off (not fundamentally, but because of lack of API hooks).
Aha! I can do a make shared shared ptr without special casing it much.
We allocate a block of memory (char[sizeof(T)]) with a destructor that converts the buffer to T then calls delete, in-place construct in that buffer (getting the T*), then convert to a shared_ptr<T> via the shared_ptr<T>( shared_ptr<char[sizeof(T)]>, T* ) constructor. With careful exception catching this should be safe, and we can emplace using our emplacement function into a make_shared combined buffer.
I am trying to use a FILE pointer multiple times through out my application
for this I though I create a function and pass the pointer through that. Basically I have this bit of code
FILE* fp;
_wfopen_s (&fp, L"ftest.txt", L"r");
_setmode (_fileno(fp), _O_U8TEXT);
wifstream file(fp);
which is repeated and now instead I want to have something like this:
wifstream file(SetFilePointer(L"ftest.txt",L"r"));
....
wofstream output(SetFilePointer(L"flist.txt",L"w"));
and for the function :
FILE* SetFilePointer(const wchar_t* filePath, const wchar_t * openMode)
{
shared_ptr<FILE> fp = make_shared<FILE>();
_wfopen_s (fp.get(), L"ftest.txt", L"r");
_setmode (_fileno(fp.get()), _O_U8TEXT);
return fp.get();
}
this doesn't simply work. I tried using &*fp instead of fp.get() but still no luck.
You aren't supposed to create FILE instances with new and destroy them with delete, like make_shared does. Instead, FILEs are created with fopen (or in this case, _wfopen_s) and destroyed with fclose. These functions do the allocating and deallocating internally using some unspecified means.
Note that _wfopen_s does not take a pointer but a pointer to pointer - it changes the pointer you gave it to point to the new FILE object it allocates. You cannot get the address of the pointer contained in shared_ptr to form a pointer-to-pointer to it, and this is a very good thing - it would horribly break the ownership semantics of shared_ptr and lead to memory leaks or worse.
However, you can use shared_ptr to manage arbitrary "handle"-like types, as it can take a custom deleter object or function:
FILE* tmp;
shared_ptr<FILE> fp;
if(_wfopen_s(&tmp, L"ftest.txt", L"r") == 0) {
// Note that we use the shared_ptr constructor, not make_shared
fp = shared_ptr<FILE>(tmp, std::fclose);
} else {
// Remember to handle errors somehow!
}
Please do take a look at the link #KerrekSB gave, it covers this same idea with more detail.