When I trace calls to the member functions of my custom allocator class,
as used by an STL container,
I notice some strange behavior: there is a call to my allocator class's
destructor that isn't matched by any earlier constructor call.
How is that possible?
Here's my code:
/*
g++ -pedantic -W -Wall -Werror screwy_allocator_example.cc -o screwy_allocator_example
*/
#include <iostream>
#include <vector>
namespace {
template<class T>
class MyAllocator {
public:
using value_type = T;
template<typename U> struct rebind { using other = MyAllocator<U>; };
MyAllocator() {
std::cout << " in MyAllocator ctor(this="<<this<<")" << std::endl;
std::cout << " out MyAllocator ctor(this="<<this<<")" << std::endl;
}
~MyAllocator() {
std::cout << " in MyAllocator dtor(this="<<this<<")" << std::endl;
std::cout << " out MyAllocator dtor(this="<<this<<")" << std::endl;
}
template<typename From>
MyAllocator(const From &from) {
std::cout << " in MyAllocator copy ctor("
<< "this="<<this<<", &from="<<(void*)&from<<")" << std::endl;
std::cout << " out MyAllocator copy ctor("
<< "this="<<this<<", &from="<<(void*)&from<<")" << std::endl;
}
template<typename From>
MyAllocator &operator=(const From &from) {
std::cout << " in MyAllocator operator=("
<< "this="<<this<<", &from="<<(void*)&from<<")" << std::endl;
std::cout << " out MyAllocator operator=("
<< "this="<<this<<", &from="<<(void*)&from<<")" << std::endl;
return *this;
}
T *allocate(size_t n) {
std::cout << " in MyAllocator::allocate("
<< "this="<<this<<", n="<<n<<")" << std::endl;
// assume n*sizeof(T) doesn't overflow
T *answer = (T*)std::malloc(n * sizeof(T));
// assume answer!=nullptr
std::cout << " out MyAllocator::allocate(this="<<this<<", n="<<n<<")"
<< ", returning "<<(void*)answer << std::endl;
return answer;
}
void deallocate(T *p, size_t n) {
std::cout << " in MyAllocator::deallocate("
<< "this="<<this<<", p="<<(void*)p<<", n="<<n<<")" << std::endl;
std::free(p);
std::cout << " out MyAllocator::deallocate("
<< "this="<<this<<", p="<<(void*)p<<", n="<<n<<")" << std::endl;
}
template<typename U> bool operator==(const MyAllocator<U> &) const
{ return true; }
template<typename U> bool operator!=(const MyAllocator<U> &) const
{ return false; }
}; // class MyAllocator<T>
} // namespace
int main(const int, char**const) {
std::cout << "in main" << std::endl;
{
std::cout << " constructing my_allocator" << std::endl;
MyAllocator<double> my_allocator;
std::cout << " constructed my_allocator" << std::endl;
{
std::cout << " constructing v(my_allocator)" << std::endl;
std::vector<double, MyAllocator<double>> v(my_allocator);
std::cout << " constructed v(my_allocator)" << std::endl;
std::cout << " pushing one item onto v" << std::endl;
v.push_back(3.14);
std::cout << " pushed one item onto v" << std::endl;
std::cout << " destructing v(my_allocator)" << std::endl;
}
std::cout << " destructed v(my_allocator)" << std::endl;
std::cout << " destructing my_allocator" << std::endl;
}
std::cout << " destructed my_allocator" << std::endl;
std::cout << "out main" << std::endl;
return 0;
}
Here's the output (it's the same for -std=c++11, -std=c++14, -std=c++17, -std=c++2a):
in main
constructing my_allocator
in MyAllocator ctor(this=0x7ffe4cee5747)
out MyAllocator ctor(this=0x7ffe4cee5747)
constructed my_allocator
constructing v(my_allocator)
constructed v(my_allocator)
pushing one item onto v
in MyAllocator::allocate(this=0x7ffe4cee5720, n=1)
out MyAllocator::allocate(this=0x7ffe4cee5720, n=1), returning 0x55890a41d2c0
pushed one item onto v
destructing v(my_allocator)
in MyAllocator::deallocate(this=0x7ffe4cee5720, p=0x55890a41d2c0, n=1)
out MyAllocator::deallocate(this=0x7ffe4cee5720, p=0x55890a41d2c0, n=1)
in MyAllocator dtor(this=0x7ffe4cee5720)
out MyAllocator dtor(this=0x7ffe4cee5720)
destructed v(my_allocator)
destructing my_allocator
in MyAllocator dtor(this=0x7ffe4cee5747)
out MyAllocator dtor(this=0x7ffe4cee5747)
destructed my_allocator
out main
Notice that there were two calls to the dtor, but only one call to the ctor:
first the MyAllocator ctor got called, with this=0x7ffe4cee5747
then a second mystery MyAllocator object appeared at this=0x7ffe4cee5720,
was used, and then was destructed (without ever having been constructed!?)
then the first MyAllocator object at this=0x7ffe4cee5747
(the one that was constructed) gets destructed as expected.
What's going on here?
It seems the simplest explanation would be that I'm just forgetting
about some flavor of constructor that the compiler generates for me.
Is that it?
If not, here are some other thoughts.
I know that prior to c++11, allocator objects were required to be "stateless".
I'm not sure precisely what that means, but perhaps it could be argued
that it implies that, prior to c++11, the compiler could be justified
in playing games like making bytewise copies of allocator objects,
skipping the constructor calls?
But, regardless, in c++11 and later,
allocators are supposedly allowed to have state, right?
Given that, I don't see how it can make any sense for the constructor call
to be skipped.
Given this strange behavior,
it seems to me that I have no choice but to implement
my allocator using the pre-c++11 restrictions:
that is, the allocator must be nothing more than
a stateless handle to some other resource.
That's what I'm doing, successfully, but I'd like to understand why this
is necessary.
Related
This question is for understanding the effect of a class that's used for RAII; specifically if a variable of that class is allocated at the beginning of a function, is the destructor only called when exiting the function (by either return or throw) or can a conforming compiler call the destructor earlier? That is: in a C++ function, is it possible for the compiler to re-use space on the stack if a variable isn't used beyond a certain point, or is this strictly controlled by '{...}'?
Here's an example, with three variants of code for controlling scope/lifetime of a local variable. Is it possible for a conforming implementation to give the same output for f1(), f2(), f3(); or is the output given below guaranteed? (f1(), f2(), f3() are identical except for where local scope {...} are used.)
#include <iostream>
#include <string>
class Raii {
public:
explicit Raii(const std::string& name)
: name_(name) {
std::cout << "Construct " << name_ << std::endl;
}
~Raii() {
std::cout << "Destruct " << name_ << std::endl;
}
std::string name_;
};
void f1() {
[[maybe_unused]] Raii r1("f1-one");
std::cout << "after f1-one" << std::endl;
[[maybe_unused]] Raii r2("f1-two");
std::cout << "after f1-two" << std::endl;
}
void f2() {
{
[[maybe_unused]] Raii r1("f2-one");
}
std::cout << "after f2-one" << std::endl;
[[maybe_unused]] Raii r2("f2-two");
std::cout << "after f2-two" << std::endl;
}
void f3() {
{
[[maybe_unused]] Raii r1("f3-one");
}
std::cout << "after f3-one" << std::endl;
{
[[maybe_unused]] Raii r2("f3-two");
}
std::cout << "after f3-two" << std::endl;
}
int main() {
std::cout << "=== f1" << std::endl;
f1();
std::cout << std::endl << "=== f2" << std::endl;
f2();
std::cout << std::endl << "=== f3" << std::endl;
f3();
std::cout << "== end" << std::endl;
return 0;
}
and I get this output with both g++ and clang++:
=== f1
Construct f1-one
after f1-one
Construct f1-two
after f1-wo
Destruct f1-two
Destruct f1-one
=== f2
Construct f2-one
Destruct f2-one
after f2-one
Construct f2-two
after f2-two
Destruct f2-two
=== f3
Construct f3-one
Destruct f3-one
after f3-one
Construct f3-two
Destruct f3-two
after f3-two
== end
here is my code snippet:
#include <iostream>
#include <list>
#include <memory>
class A
{
public:
int a = 100;
A()
{
std::cout << "Create A" << std::endl;
}
~A()
{
std::cout << "Release A" << std::endl;
}
virtual void printer() = 0;
};
std::list<std::shared_ptr<A>> arr;
class B : public A
{
public:
int b = 1000;
~B()
{
std::cout << "Release B" << std::endl;
}
void printer() override
{
std::cout << "B's printer" << std::endl;
}
B()
{
std::shared_ptr<A> tmp(this);
arr.push_back(tmp);
(*arr.begin())->printer();
std::cout << "inside B's c'tor test B counts: " << tmp.use_count()
<< std::endl;
}
};
int main(int argc, char const *argv[])
{
std::shared_ptr<B> B_ptr = std::make_shared<B>();
std::shared_ptr<A> A_ptr(B_ptr);
std::cout << "list address: " << (*arr.begin()).get() << std::endl;
std::cout << "test B address: " << B_ptr.get() << std::endl;
std::cout << "test A address: " << A_ptr.get() << std::endl;
std::cout << "list counts: " << (*arr.begin()).use_count() << std::endl;
std::cout << "test B counts: " << B_ptr.use_count() << std::endl;
std::cout << "test A counts: " << A_ptr.use_count() << std::endl;
return 0;
}
My expectation is: A's reference count should be three, but only got 2. I think when I push_back to the list, there should be a temporarily created share_ptr object, even it is get destroyed, the one in list should also pointing to the same address as A_ptr and B_ptr. It turns out that they (those three), did pointing at the same address, but use_count got different results. (*arr.begin()).use_count() is 1, the others are both 2.
Why? Please help.
Ps: I know turning this pointer to share_ptr is stupid operation, but the result doesn't make sense and even disobey the syntax.
My expectation is: A's reference count should be three, but only got 2.
Your expectation is wrong. You only made one copy of the shared pointer, so the use count is 2.
std::shared_ptr<A> tmp(this);
On this line you transfer the ownership of a bare pointer that you don't own into a new shared pointer. Since this was already owned by another shared pointer, the behaviour of the program will be undefined when the two separate owners attempt to destroy it.
Creating a shared pointer from this is possible using std::enable_shared_from_this, but it's not simple.
I'd like to take out members of a temporary without unnecessary moving or copying.
Suppose I have:
class TP {
T _t1, _t2;
};
I'd like to get _t1, and _t2 from TP(). Is it possible without copying/moving members?
I've tried with tuples and trying to "forward" (I don't think it's possible) the members, but the best I could get was a move, or members dying immediately.
In the following playground using B::as_tuple2 ends up with members dying too soon, unless the result is bound to a non-ref type, then members are moved. B::as_tuple simply moves is safe with auto on client side.
I suppose this should be technically possible, since the temporary dies immediately, and the member do die while they could bound to variables on the calling site (Am I wrong?), and structured binding of a similar struct works as intended.
Is it possible to extend/pass life of the member onto an outside variable, or elide the move/copy? I need it with c++14 version, but I couldn't get it to work on c++17 either, so I am interested in both.
Playground:
#include <tuple>
#include <iostream>
using std::cout;
class Shawty {
/**
* Pronounced shouty.
**/
public:
Shawty() : _id(Shawty::id++) {cout << _id << " ctor\n"; }
Shawty(Shawty && s) : _id(Shawty::id++) { cout << _id << " moved from " << s._id << "\n"; }
Shawty(const Shawty & s) : _id(Shawty::id++) { cout << _id << " copied from " << s._id << "\n"; }
Shawty& operator=(Shawty && s) { cout << _id << " =moved from " << s._id << "\n"; return *this;}
Shawty& operator=(Shawty & s) { cout << _id << " =copied from " << s._id << "\n"; return *this;}
~Shawty() {cout << _id << " dtor\n"; }
int _id;
static int id;
};
int Shawty::id = 0;
class B {
public:
auto as_tuple() && {return std::make_tuple(std::move(_s1), std::move(_s2));}
auto as_tuple2() && {return std::forward_as_tuple(std::move(_s1), std::move(_s2));}
private:
Shawty _s1, _s2;
};
struct S {
Shawty _s1, _s2;
};
int main() {
std::cout << "----------\n";
auto [s1, s2] = B().as_tuple2();
std::cout << "---------\n";
auto tpl1 = B().as_tuple2();
std::cout << "----------\n";
std::tuple<Shawty, Shawty> tpl2 = B().as_tuple2();
std::cout << "----------\n";
std::cout << std::get<0>(tpl1)._id << '\n';
std::cout << std::get<1>(tpl1)._id << '\n';
std::cout << std::get<0>(tpl2)._id << '\n';
std::cout << std::get<1>(tpl2)._id << '\n';
std::cout << s1._id << '\n';
std::cout << s2._id << '\n';
std::cout << "--struct--\n";
auto [s3, s4] = S{};
std::cout << s3._id << '\n';
std::cout << s4._id << '\n';
std::cout << "----------\n";
return 0;
}
No. It is not possible to extend the lifetime of more than one member beyond the lifetime of the super object.
So, the only way to "get" members without copying is to keep the super object alive, and refer to them:
// member function
auto as_tuple3() & {
return std::make_tuple(std::ref(_s1), std::ref(_s2));
}
// usage
B b;
auto [s1, s2] = b.as_tuple3();
An example of extending lifetime of the object by binding a reference to a single member. Note that this requires the member to be accessible from where the reference is bound (not the case in your example, where the member is private):
auto&& s1 = B{}._s1;
Add support for structured binding to your B type.
class B {
public:
template<std::size_t I, class Self,
std::enable_if_t< std::is_same_v<B, std::decay_t<Self>>, bool> = true
>
friend constexpr decltype(auto) get(Self&& self) {
if constexpr(I==0)
{
using R = decltype(std::forward<Self>(self)._s1)&&;
return (R)std::forward<Self>(self)._s1;
}
else if constexpr(I==1)
{
using R = decltype(std::forward<Self>(self)._s2)&&;
return (R)std::forward<Self>(self)._s2;
}
}
private:
Shawty _s1, _s2;
};
namespace std {
template<>
struct tuple_size<::B>:std::integral_constant<std::size_t, 2> {};
template<std::size_t N>
struct tuple_element<N, ::B>{using type=Shawty;};
}
Test code:
int main() {
std::cout << "----------\n";
{
auto&& [s1, s2] = B();
}
}
output:
----------
0 ctor
1 ctor
1 dtor
0 dtor
Live example.
This is the best I can do. Note that s1 and s2 are references into a lifetime-extended version of B.
When running the following code, it seems that the destructor is running twice. I have a theory that this might have to do with an automatic move constructor being added, but I'm not sure how to test this.
#include <iostream>
#include <functional>
struct Structure {
Structure(int n) :
Value(n) {
std::cout << "constructor: " << Value << std::endl;
}
~Structure() {
std::cout << "destructor: " << Value << std::endl;
}
int Value;
};
int main() {
int Init = 4;
std::function<void()> Function = [Instance = Structure(Init)] () {
std::cout << "Value is: " << Instance.Value << std::endl;
};
Function();
Function();
return 0;
}
Output:
constructor: 4
destructor: 4
Value is: 4
Value is: 4
destructor: 4
Is this output correct?
std::function works by copying the callable object you provide. There is no copy-elision here, since your lambda is not an std::function but of an anonymous, unrelated type.
Hence, the two destructors you see are:
The Instance member of the original, temporary, lambda;
The Instance member of the copy of the lambda that was stored into the std::function and lived until the end of main.
Ok, I defined the move and copy constructors manually and also saw errors when I instructed the compiler to delete them in a variant of the code below. Everything seems normal.
Revised code:
#include <iostream>
#include <functional>
struct Structure {
Structure(int n) :
Value(n) {
std::cout << "constructor: " << Value << std::endl;
}
Structure(const Structure& other) :
Value(other.Value) {
std::cout << "copy constructor: " << Value << std::endl;
}
Structure(Structure&& other) :
Value(other.Value) {
other.Value = -1;
std::cout << "move constructor: " << Value << std::endl;
}
~Structure() {
std::cout << "destructor: " << Value << std::endl;
}
int Value;
};
int main() {
int Init = 4;
std::function<void()> Function = [Instance = Structure(Init)] () {
std::cout << "Value is: " << Instance.Value << std::endl;
};
Function();
Function();
return 0;
}
Output:
constructor: 4
move constructor: 4
destructor: -1
Value is: 4
Value is: 4
destructor: 4
Currently I do the following:
// float *c_array = new float[1024];
void Foo::foo(float *c_array, size_t c_array_size) {
//std::vector<float> cpp_array;
cpp_array.assign(c_array, c_array + c_array_size);
delete [] c_array;
}
How can I optimize this assigning? I would like not to perform elementwise copy but just swap pointers.
The current std::vector doesn't provide any capability or interface to take ownership of previously allocated storage. Presumably it would be too easy to pass a stack address in by accident, allowing more problems than it solved.
If you want to avoid copying into a vector, you'll either need to use vectors through your entire call chain, or do it the C way with float[] the entire time. You can't mix them. You can guaranteed that &vec[0] will be equivalent to the C-array though, fully contiguous, so using vector in the whole program may be feasible.
Currently, the std::vector interface does not possess the capacity to move from or swap with anything except another std::vector.
The only way to do it would be to create a custom allocator.
Write an allocator class that you can initialise with your array.
Instantiate the vector with the allocator as an argument.
Unlikely it's possible - it's quite dangerous, because std::vector doesn't know how the memory was allocated and how it should be freed.
If it's possible, you may replace original allocation with creation of std::vector of correct size. It uses contiguous memory area, so it can replace manually allocated buffer.
It is possible with the use of a custom allocator. I checked on godbolt.org with clang and gcc. To me, it looks a bit of an ugly hack – but it works at least as a proof of concept.
Of course, you have to take care of the lifetime of the array for yourself.
#include <vector>
#include <iostream>
// custom allocator
template <typename T>
class my_allocator {
private:
T* const addr;
public:
template <typename U>
struct rebind {
typedef my_allocator<U> other;
};
//provide the required no-throw constructors / destructors:
constexpr my_allocator(T* addr_) : addr(addr_) { };
constexpr my_allocator(const my_allocator<T>& rhs) : addr(rhs.addr) { };
template <typename U>
my_allocator(const my_allocator<U>& rhs, T* addr_) : addr(addr_) { };
~my_allocator() { };
//import the required typedefs:
using value_type=T;
using pointer=T*;
using reference=T&;
using const_pointer=const T*;
using const_reference=const T&;
using size_type=size_t;
using difference_type=ptrdiff_t;
constexpr pointer allocate(size_type n, const void * = 0) {
pointer t=addr;
std::cout
<< " used my_allocator to allocate at address "
<< t << " (+)" << std::endl;
return addr;
}
constexpr void deallocate(void* p, size_type) {
if (p) {
std::cout
<< " used my_allocator to deallocate at address "
<< p << " (-)" <<
std::endl;
}
}
template< class U, class... Args >
void construct( U* p, Args&&... args ) {
// avoids initialisation of the elements.
std::cout << "Contruct called" << std::endl;
}
};
// helper function for easy useage
template<typename T>
const std::vector<T, my_allocator<T> > CreateVectorFromArray(T* array, int size) {
const my_allocator<T> alloc=my_allocator<T>(array);
std::vector<int, my_allocator<int> > vecAll(size, my_allocator<int>(array));
return vecAll;
}
template<typename T>
using element_type_t = std::remove_reference_t<decltype(*std::begin(std::declval<T&>()))>;
template<typename AR>
auto CreateVectorFromArrayAr(AR& array) {
using T=element_type_t<AR>;
const my_allocator<T> alloc=my_allocator<T>(array);
std::vector<T, my_allocator<T> > vecAll(sizeof(array)/sizeof(array[0]), my_allocator<T>(array));
return vecAll;
}
int main() {
int array[]={0,1,2,3,4,5,6,7,8,9};
std::cout << "Array: " << &array[0] << " " << array[0] << " " << array[1]<< " " << array[2] << std::endl;
auto vecAll=CreateVectorFromArray(array, sizeof(array)/sizeof(array[0]));
auto vec3=CreateVectorFromArrayAr(array);
std::cout << "Vector: " << &vecAll[0] << " " << vecAll[0] << " " << vecAll[1]<< " " << vecAll[2] << std::endl;
std::cout << "Array: " << &array[0] << " " << array[0] << " " << array[1] << " " << array[2] << std::endl;
std::cout << "vec3: " << &vec3[0] << " " << vec3[0] << " " << vec3[1] << " " << vec3[2] << std::endl;
std::cout << "sizeof(vecAll)=" << sizeof(vecAll) << std::endl;
std::cout << "sizeof(void*)=" << sizeof(void*) << std::endl;
return 0;
}