Question

I am using arcsynthesis' tutorial to learn modern 3D graphics programming, and while I am understanding most of it I have run into a snag with what the author calls "complex management code" I can't understand how this code works:

struct Instance
{
    typedef glm::vec3(*OffsetFunc)(float);

    OffsetFunc CalcOffset;

    glm::mat4 ConstructMatrix(float fElapsedTime)
    {
        glm::mat4 theMat(1.0f);

        theMat[3] = glm::vec4(CalcOffset(fElapsedTime), 1.0f);

        return theMat;
    }
};


Instance g_instanceList[] =
{
    {StationaryOffset},
    {OvalOffset},
    {BottomCircleOffset},
};

I don't quite get how the function pointer is getting set inside the struct in the g_instanceList[] I don't get syntactically how this works and then how this g_instanceList is used in the next segment here:

float fElapsedTime = glutGet(GLUT_ELAPSED_TIME) / 1000.0f;
    for(int iLoop = 0; iLoop < ARRAY_COUNT(g_instanceList); iLoop++)
    {
        Instance &currInst = g_instanceList[iLoop];
        const glm::mat4 &transformMatrix =
currInst.ConstructMatrix(fElapsedTime);

        glUniformMatrix4fv(modelToCameraMatrixUnif, 1, GL_FALSE,     glm::value_ptr(transformMatrix));
    glDrawElements(GL_TRIANGLES, ARRAY_COUNT(indexData), GL_UNSIGNED_SHORT, 0);
}

I thought I was pretty familiar with c++ by this point but this syntactic sugar is new to me. Thanks in advance :)

Was it helpful?

Solution

I don't quite get how the function pointer is getting set inside the struct in the g_instanceList[]

Instance is an aggregate type. It may have a member function, but that doesn't make it not an aggregate. In C++98/03, objects of aggregate types can be initialized with aggregate initialization:

struct Aggregate
{
  int x;
  int y;
};

Aggregate agg = { 5, 4 };
assert(4 == agg.y); //Will be true.

Each member in the struct is initialized with each individual value, in the order in which those members are declared in the struct. The 5 goes into x and the 4 goes into y.

Instance is an aggregate. And it has exactly one member. That member happens to be a function pointer type, but C++ doesn't care; it's a value and it can be used in aggregate initialization.

A C++ array is an aggregate as well. So you can initialize an array with aggregate initialization:

int arr[4] = {3, 45, 9, 81};

C++ also lets you initialize an array with aggregate initialization, where the size of the array is determined by the initializer:

int arr[] = {5, 2};
//arr will have 2 elements.

Therefore, g_instanceList is an array, and therefore an aggregate which is subject to aggregate initialization. It has a size that will be determined by the number of values provided by the aggregate initialization syntax. Each element of the array is also an aggregate, so each element of the array can therefore be aggregate initialized.

Instance g_instanceList[] =
{
    {StationaryOffset},
    {OvalOffset},
    {BottomCircleOffset},
};

Each member of the aggregate initialization list is itself an aggregate initialization which initializes an Instance object. StationaryOffset and the rest are function pointers which match the signature of the member of Instance. So that's how Instance::CalcOffset gets filled in.


how this g_instanceList is used in the next segment here

How it gets used is pretty common C++. The first line gets a reference to one of the Instance array elements. This is done just to make the code shorter; it doesn't have to use g_instanceList[iLoop] every time to talk about the Instance it's using. The second line calls the member function of Instance (which itself calls the internal function pointer), storing the result in a variable.

If you're talking about ARRAY_COUNT, that's just a convenient macro to get... well, the count of the array elements. The C++ compiler knows how big g_instanceList is, so the macro is used to make the compiler compute it itself. That way, if the g_instanceList array length changes, you don't have to go through the entire codebase to find every use of the array's length. Also, you never have to directly state the length; you can infer it through aggregate initialization.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top