1

I have some function where I need to use a member variable(a vector of custom classes).
At the end of this function this member needs to be cleared but it needs to stay as a member for the duration of this function. Another problem is that the function can end prematurely due to custom error handling of the program. Yet the member still needs to be cleared.

I first moved this member at the beginning in a local variable using std::move. This worked pretty well but it now turns out I need the variable to stay as a member variable till the end of that function.

I came up with the following solution using a unique_ptr with a reference that will do the move upon destruction.

#include <iostream>
#include <vector>
#include <memory>
using namespace std;

template <class T>
class Clean
{
public:
    Clean(T& clean)
        : m_clean(clean) {}
    ~Clean()
    {
        T destroy = move(m_clean);
    }
private:
    T& m_clean;
};

class A
{
public:
    A()
    {
        m_numbers = { { 3, 1 ,4, 1, 5} };
    }

    void display()
    {
        auto cleanNumbers = make_unique<Clean<vector<int> > >(m_numbers);
        for(int number: m_numbers)
            cout << number << endl;
    }
private:
    vector<int> m_numbers;
};

int main()
{
    A a;
    a.display();
    cout << "should be empty now" << endl;
    a.display();
    return 0;
}

Any cleaner solutions are also welcome but my actual question is the following.
Is there any std equivalent of the Clean class I used?

Ps: code fragment compiles for me using g++ 5.3.0

g++ -std=c++14 -o main main.cpp

turoni
  • 1,345
  • 1
  • 18
  • 37
  • Also I know this will only work when the move constructor is implemented for a certain class but that is okay in my case. – turoni Jul 18 '17 at 09:08
  • Unique pointers can be constructed to invoke custom deleters as stateless lambdas or functions for resource destruction. – Ron Jul 18 '17 at 09:24
  • @Ron I know, that's exactly what I have done in my code to give Clean the correct behavior . My question is if there is an std equivalent of this clean class. – turoni Jul 18 '17 at 09:25
  • How would you use a lambda to achieve this? – turoni Jul 18 '17 at 09:29
  • 2
    Scott Meyers _Effective Modern C++ - item 18_ also mentioned in [this SO post](https://stackoverflow.com/questions/42715492/stdunique-ptr-and-custom-deleters). – Ron Jul 18 '17 at 09:38

2 Answers2

1

You could use a shared_ptr's custom deleter to obtain the same result.

void A::display()
{
    std::shared_ptr<int> dummy (
        (int*)alloca(sizeof(int)),                 // very fast allocate on stack
        [&](int*) { this->m_numbers.clear(); }
    );

    for(int number: m_numbers)
        cout << number << endl;
}

Here is the entire code, compiles fine, gcc 5.3 -Wall -Wpedantic -march=native -std=c++14

#include <iostream>
#include <vector>
#include <memory>
using namespace std;

class A
{
public:
    A()
    {
        m_numbers = { { 3, 1 ,4, 1, 5} };
    }

    void display()
    {
        std::shared_ptr<int> dummy (
            (int*)alloca(sizeof(int)),
            [&](int*) { this->m_numbers.clear(); }
        );

        for(int number: m_numbers)
            cout << number << endl;
    }
private:
    vector<int> m_numbers;
};

int main()
{
    A a;
    a.display();
    cout << "should be empty now" << endl;
    a.display();
    return 0;
}
Michaël Roy
  • 6,338
  • 1
  • 15
  • 19
  • I would use a `shared_ptr` rather than messing with the non-portable `alloca`. – aschepler Jul 18 '17 at 11:02
  • This does not compile using g++ 5.3.0: g++ -std=c++17 -o main main.cpp – turoni Jul 18 '17 at 11:05
  • You could., but you'd pay the price for allocating from the heap. alloca is certainly more portable than using nullptr, and pray that your compiler won't initialize `dummy`. gcc, MSVC, and clang do support alloca. – Michaël Roy Jul 18 '17 at 11:08
  • @MichaëlRoy Thanks for the effort but I cannot seem to use it in g++. I get the following error: >g++ -Wall -Wpedantic -march=native -std=c++14 -o main3 main3.cpp main3.cpp: In member function 'void A::display()': main3.cpp:17:37: error: 'alloca' was not declared in this scope (int*)alloca(sizeof(int)), – turoni Jul 18 '17 at 11:19
  • I am not gonna downvote you for it but I need a more general solution that works on most platforms since this is just example code and in the actual implementation it will need to compile in visual c++ and clang. – turoni Jul 18 '17 at 11:21
  • So it does. Both clang and msvc support alloca, – Michaël Roy Jul 18 '17 at 11:26
1

This is the result I came to thanks to comments and other questions:

void display()
{
    auto cleanNumber = [](decltype(m_numbers)* numbers){ 
        if(numbers) 
            numbers->clear();
    };
    auto pClean = std::unique_ptr<decltype(m_numbers), decltype(cleanNumber)>(&m_numbers, cleanNumber);
    for(int number: m_numbers)
        cout << number << endl;
}
turoni
  • 1,345
  • 1
  • 18
  • 37