1

I don't quite understand what the following does:

 std::vector<const char*> getRequiredExtensions()
{
    uint32_t glfwExtensionCount = 0;
    const char** glfwExtensions;
    glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

    std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);

    if (enableValidationLayers) {
        extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
    }

    return extensions;
}

I don't understand this line:

std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);

What exactly does it do? And what does glfwExtensions resolve to, I only get that it's a double pointer (Which is also a new concept for me). And what does the glfwExtensions + glfwExtensionCount mean?

thanks for any help!

Reskareth
  • 47
  • 4
  • have you read some documentation on `std::vector`? If yes, where does the confusion arise from? – Marcus Müller Sep 08 '22 at 15:27
  • First of all, I'm new to c++. So I don't understand how these double pointers work. – Reskareth Sep 08 '22 at 15:32
  • trick with double pointers is: ignore the fact that they're double pointers, that's not a "special thing" at all. It's really just a pointer, and the thing it points at happens to be a pointer. That's all. Replace, mentally `const char*` with `some_type`, and your complicated `const char** glfwExtensions;` becomes `some_type* glfwExtensions;`, and that's much less confusing, right? – Marcus Müller Sep 08 '22 at 15:34
  • It's using _overload 5_ @ [`std::vector::vector`](https://en.cppreference.com/w/cpp/container/vector/vector) (the one taking two iterators) – Ted Lyngmo Sep 08 '22 at 15:36

3 Answers3

4

Let's concentrate on the following line of code:

std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);

This declares extensions as a variable of the templated std::vector<typename T> type, where the T type resolves to const char* (that is, it declares a vector whose elements are each pointers to constant character data). This vector object is initialized using the constructor that takes two iterator arguments – the form (5) on this cppreference page.

A point of confusion here may be how the arguments are iterators: well, pointers are also iterators (in many ways). In this case, the const char** glfwExtensions; line declares a variable that points to a const char* (which is the type of the vector's elements), and the call to glfwGetRequiredInstanceExtensions makes this point to the beginning of an array of such (const char*) pointers and sets the glfwExtensionCount variable to the number of elements in that array. Further, adding the size of an array to a pointer to its first elements yields a pointer to "one past the end" of the array – which is equivalent to an STL "end" iterator.

Thus, the two arguments to the constructor are, effectively, acting as std::begin(glfwExtensions) and std::end(glfwExtensions) – but those functions can't be used directly, because glfwExtensions is not an array of known size in this context.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
1

It may help if you compare const char** glfwExtensions; with the famous variable argv in main:

int main(int argc, char **argv) //  or:  char *argv[]

glfwExtensions can likewise be seen as an array of const char*

So, after

glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

you can therefore

for(uint32_t i = 0; i < glfwExtensionCount; ++i) {
    // access   glfwExtensions[i]    here
    // or     *(glfwExtensions + i)  (same thing)
}

Since glfwExtensions is a const char**, then glfwExtensions + i is also a const char**, i steps from glfwExtensions + 0. The asterisk in *(glfwExtensions + i) means that you dereference the pointer to get a reference to the value it's pointing at - which, since you have a const char**, is a const char*.

glfwExtensions is a pointer to the first element in glfwExtensionCount number of elements and you can use glfwExtensions + i to get a pointer to any of those elements.

The line you wonder about

std::vector<const char*> extensions(glfwExtensions,
                                    glfwExtensions + glfwExtensionCount);

is using the vector constructor that takes two iterators and populates the vector with the dereferenced values, the actual const char*s.

glfwExtensions + glfwExtensionCount is pointing one element past the last element. You are only allowed to deference the elements in the range [glfwExtensions + 0, glfwExtensions + glfwExtensionCount - 1] so this pointer is used to tell the vector where to stop reading values.

It's similar to doing this:

const char** current = glfwExtensions;
const char** one_past_last = glfwExtensions + glfwExtensionCount;        

std::vector<const char*> extensions;
extensions.reserve(std::distance(current, one_past_last));

for(;current != one_past_last; ++current) // stop at one_past_last
    // dereference current to get the value it is pointing at:
    extensions.push_back(*current);
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
0

So...

const char** glfwExtensions;

It is a pointer to a pointer (pointer to string) This mean that when you to this...

glfwExtensions + glfwExtensionCount

The: const char** glfwExtensions; is like a vector and with + count you access its next element.

To simplify the: glfwExtensions + glfwExtensionCount is like vector::at(glfwExtensionCount)

And basicly in this code:

std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);

You are creating std::vector from begin which is glfwExtensions to end which is glfwExtensions + glfwExtensionCount

Feniks
  • 49
  • 3