Home > OS >  C : How to create a "conditional" constructor/avoid destruct on assignment?
C : How to create a "conditional" constructor/avoid destruct on assignment?

Time:11-11

Let's start with some example code:


class Shader {
public:

    static absl::StatusOr<Shader> Create(const std::string &vertexShaderFile, const std::string &fragmentShaderFile) {
        ASSIGN_OR_RETURN(GLuint vertexShader, gl::createShader(GL_VERTEX_SHADER, readFile(vertexShaderFile)))
        ASSIGN_OR_RETURN(GLuint fragmentShader, gl::createShader(GL_FRAGMENT_SHADER, readFile(fragmentShaderFile)))

        Shader s;
        glAttachShader(s._programId, vertexShader);
        glAttachShader(s._programId, fragmentShader);
        RETURN_IF_ERROR(gl::linkProgram(s._programId));
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);

        std::cerr << "AAA" << std::endl;
        return s;
    }

    void use() const {
        std::cerr << "use " << _programId << std::endl;
        glUseProgram(_programId);
    }

    ~Shader() {
        std::cerr << "destruct!" << std::endl;
        glDeleteProgram(_programId);
    }

private:
    explicit Shader() : _programId(glCreateProgram()) {}
    GLuint _programId{0};
};

usage:

const auto &_statusOr4 = (Shader::Create("resources/vertex.glsl", "resources/frag.glsl"));
if (!_statusOr4.ok())return _statusOr4.status();
Shader shader = *_statusOr4;

std::cerr << "BBB" << std::endl;

This will print:

AAA
destruct!
BBB

I think the s in Shader::Create is being copied to a new object and then the old one is destructed, which calls the ~Shader destructor.

I want to avoid the copy destruct.

I can't just throw all the Create code in the c'tor because there's a few operations that might fail.

How can I do this?


Adding

    Shader(const Shader&) = delete;
    Shader& operator=(const Shader&) = delete;

Prevents this program from compiling. Which is fine, because this is indeed an error, but then I still don't know how to write such a Create method (without moving everything into a constructor and throwing exceptions instead)


I tried to add a move constructor:

    Shader(Shader&& o) noexcept : _programId(o._programId){
        std::cerr << "move ctor" << std::endl;
    }

Had to change the assignment to make it a reference:

const auto &_statusOr4 = (Shader::Create("resources/vertex.glsl", "resources/frag.glsl"));
if (!_statusOr4.ok()) return _statusOr4.status();
const Shader& shader = *_statusOr4;

Which I think should be fine because _statusOr4 will have the same lifetime.

However.... now I get

AAA
move ctor
destruct!
BBB

It seems the destructor is still called on the object that it was "moved" out of.


StatusOr is from Abseil

CodePudding user response:

I want to avoid the copy destruct.

Normally, when your function return type matches the type of the variable you return, you don't need to do anything. There is no copying and no destruction taking place. Read about copy elision.

In your case, you construct Shader but return absl::StatusOr<Shader>. So there are two different objects: the local variable s in your function, and the return value. They are of different types so the compiler cannot make them the same thing, and you will see the destructor of s being called. However this is harmless if you write Shader correctly. Support move construction, and s will be moved to the return value, leaving behind an empty shell that is trivial to destroy. There is still no copying.

Assuming that 0 is an invalid/empty shader program and glDeleteProgram(0) is essentially a no-op, the move constructor could look like this:

Shader(Shader&& other) {
   _programId = other._programId;
   other._programId = 0;
}

You don't want to freely copy objects Shader objects, because otherwise you will end up deleting the same _programId more than once. To avoid this, mark the copy constructor and the copy assignment operator as delete.

I can't just throw all the Create code in the c'tor because there's a few operations that might fail.

That's what exceptions are for.

  •  Tags:  
  • c
  • Related