简体   繁体   中英

How do I assign to a const variable using an out parameter in C++?

In a class header file Texture.h I declare a static const int .

static const int MAX_TEXTURE_SLOTS;

In Texture.cpp I define the variable as 0 .

const int Texture::MAX_TEXTURE_SLOTS = 0;

Now in Window.cpp class's constructor I attempt to assign to the variable using an out parameter, however this obviously does not compile as &Texture::MAX_TEXTURE_SLOTS points to a const int* and not an int* .

glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &Texture::MAX_TEXTURE_SLOTS);

I have tried using const_cast , but am greeted with a segmentation fault on runtime.

glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, const_cast<int*>(&Texture::MAX_TEXTURE_SLOTS));

I have also tried directly casting to an int * but once again, seg fault.

glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, (int*)&Texture::MAX_TEXTURE_SLOTS);

Many thanks.

Provided that glGetIntegerv doesn't depend on other gl* functions being called before, you may use an immediately-invoked lambda:

// Texture.cpp

const int Texture::MAX_TEXTURE_SLOTS = []
{
    int maxTextureSlots{0}:
    glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureSlots);
    return maxTextureSlots;
}();

EDIT 2: So since you're trying to abstract OpenGL contexts, you'll have to let go of the "traditional" constructor/destructor idioms. And just for your information (unrelated to this question): OpenGL contexts are not tied to windows! As long as a set of windows and OpenGL contexts are compatible with each other, you may mix and match any way you like. But I digress.

The standard idiom to deal with a situation like yours is to use preinitializing factory functions. Like this:

class MyOpenGLContextWrapper {
public:
    // Yes, shared_ptr; using a unique_ptr here for objects that are kind
    // of a nexus for other things -- like an OpenGL context -- just creates
    // a lot of pain and misery. Trust me, I know what I'm talkink about.
    typedef std::shared_ptr<MyOpenGLContextWrapper> ptr;

    struct constdata {
        NativeGLContextType context;
        // ...
        GLint max_texture_image_units;
        // ...
    };

    static ptr create();

protected:
    MyOpenGLContextWrapper(constdata const &cdata) : c(cdata) {};
    virtual ~MyOpenGLContextWrapper();
    constdata const c;
}

MyOpenGLContextWrapper::ptr MyOpenGLContextWrapper::create()
{
    struct object : public MyOpenGLContextWrapper {
        object(MyOpenGLContextWrapper::constdata const &cdata) : MyOpenGLContextWrapper(cdata) {}
        ~object(){}
    };

    MyOpenGLContextWrapper::constdata cdata = {};

    // of course this should all also do error checking and failure rollbacks
    cdata.context = create_opengl_context();
    bind_opengl_context(cdata.context);
    // ...
    glGetInteger(GL_MAX_TEXTURE_IMAGE_UNITS, &cdata.max_texture_image_units);

    return std::make_shared<object>(cdata);
}

EDIT: I just saw that you intend to use this to hold on to a OpenGL limit. In that case you can't do this on a global scope anyway, since those values depend on the OpenGL context in use. A process may have several OpenGL contexts, each with different limits.


On most computer systems you'll encounter these days, variables declared const in global scope will be placed in memory that has been marked as read only . You literally can't assign to such a variable.

The usual approach to implement global scope runtime constants is by means of query functions that will return from an internal or otherwise concealed or protected value. Like

// header.h
int runtime_constant();
#define RUNTIME_CONSTANT runtime_constant()

// implementation.c / .cpp
int runtime_constant_query(){
    static int x = 0;
    // NOTE: This is not thread safe!
    if( !x ){ x = determine_value(); }
    return x;
}

You can then fetch the value by calling that function.

You don't.

You can't assign to a const outside of its definition. Also, using a const variable where the const has been const_cast ed away is UB. This also means you can't directly initialize a const variable with an output parameter. For trivial types, just output to another variable and make a const copy if you so wish.

If you were the author of the function you're calling, you would do well not to use out parameters, and then you could assign to const variables directly, perhaps using structured bindings if you want to name multiple of the outputs at a time. But here, you're not.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM