0

I have not been able to find an answer on this, but my case is:

// vulkan_glfw_backend.hpp
struct alignas(8) VulkanGlfwWindowContext;

class MY_API VulkanGlfwBackend
{
    // [...]
private:
    VulkanGlfwWindowContext* mpContext;
};

And the source file, where I want to have the implementation:

// vulkan_glfw_backend.cpp
#include "vulkan_glfw_backend.hpp"

struct VulkanGlfwWindowContext
{
    int numWindows;
    GLFWwindow* windows[MAX_WINDOWS];
};

Initially, my compiler complained because it couldn't determine the alignment requirements for the class, which I suppose makes sense. Then I added the alignas attribute. But now I get an error message, which I cannot understand the logical reason for:

vulkan_glfw_backend.cpp:113:51: error: invalid use of incomplete type ‘struct VulkanGlfwWindowContext’
  113 |                 for (int i = 0; i < mpContext->numWindows; i++)

Since I declare it explicitly as a pointer inside a class, the storage requirements should be clear. Additionally, it's an implementation detail how the memory layout of the struct looks, which the compiler knows at compile-time, because it is defined at the top of the source file.

So, why is this going wrong, and can I fix it somehow? For several reasons I desire to have an opaque type. Thanks!

alexpanter
  • 1,222
  • 10
  • 25
  • 1
    That code should be fine, even without the `alignas(8)`. Assuming that the structure is actually defined in `vulkan_glfw_backend.cpp`. What *is* your compiler? What version? What is `MY_API`? – Some programmer dude May 26 '22 at 12:35
  • 3
    Worked on my machine. The code snippets are not a [mcve], so I probably added the missing pieces without adding the bug that is not in the code provided. – Eljay May 26 '22 at 12:39
  • 1
    There are nothing wrong with what you describe so it is probably not what your code actually does. – Öö Tiib May 26 '22 at 12:41
  • @Someprogrammerdude I'm using C++20 with GCC-11. `MY_API` is a platform-specific dll import/export decorator since I'm exporting a shared library - pretty sure that's not the problem here. – alexpanter May 26 '22 at 12:56

1 Answers1

0

Answering my own question :)

So this is one of those days, where I'm not completely posting the entire code as is (my apologies!). So, the entire thing is inside a namespace:

// header
namespace mynamespace
{
    // vulkan_glfw_backend.hpp
    struct alignas(8) VulkanGlfwWindowContext;
}

//source
#include "vulkan_glfw_backend.hpp"
using namespace mynamespace;

struct VulkanGlfwWindowContext
{
    int numWindows;
    GLFWwindow* windows[MAX_WINDOWS];
};

The reason for this going wrong - again, I don't understand the logics behind it. Anyways, this is the fix:

//source
struct mynamespace::VulkanGlfwWindowContext
{
    int numWindows;
    GLFWwindow* windows;
};

Apparently, C++ doesn't understand the definition without fully-qualified namespace prepended to the name. I have seen the same issue with functions - it is not enough to open the namespace, it must be included in the definition.

This confuses me, as it is not required when implementing class member functions, where the name of the class is sufficient, e.g. someclass::somemember() {}.

But it works now!

alexpanter
  • 1,222
  • 10
  • 25
  • 2
    The statement `using namespace mynamespace;` doesn't make `mynamespace` the current namespace. It only takes all symbols defined in the `mynamespace` namespace, and adds them to the current namespace. Since the current namespace is the global namespace, the definition `struct VulkanGlfwWindowContext { ... };` will define `VulkanGlfwWindowContext` in the global namespace and not any other namespace. – Some programmer dude May 26 '22 at 13:34
  • @Someprogrammerdude That actually makes a lot of sense. Thank you for clarifying that!! :) – alexpanter May 26 '22 at 13:46
  • 2
    Don't use `using namespace`, this does not "open" a namespace (it just pollutes the current namespace, as @Someprogrammerdude has explained). Instead, you can just open and then at some point extend (reopen) the same namespace (by writing `namespace mynamespace { /* ... more stuff ... */ }`) many times, in different places. See [Namespaces](https://en.cppreference.com/w/cpp/language/namespace) for some examples there. – heap underrun May 26 '22 at 13:54
  • @heapunderrun That's a good point. But the only reason I'm not doing that is that it leads to extra indentation in source files, which in my opinion [!] makes the code less readable. In this particular case, though, it would also be a good fix. Maybe I'm brain-damaged from writing too much C#, where not indenting after a namespace declaration looks wrong to me. :) – alexpanter May 26 '22 at 13:59
  • @heapunderrun with my current "opening" of namespaces - would that, theoretically speaking, induce longer compilation times? – alexpanter May 26 '22 at 14:01
  • Regarding indentation: I also prefer to avoid adding any additional indent just for the namespace's sake (except perhaps only in cases when using complex grid of nested namespaces). When your IDE auto-indents the start of the line following the opening brace of the namespace, just press backspace to unindent, and the following lines won't have that problem. At least, in Visual Studio, this is how it works for me. – heap underrun May 26 '22 at 14:07
  • And if you are using [Clang-Format](https://clang.llvm.org/docs/ClangFormatStyleOptions.html) then just set `NamespaceIndentation` to either `None` or `Inner` (but not to `All`). – heap underrun May 26 '22 at 14:17
  • @heapunderrun I'm using Emacs, so the indentation configuration is slightly more difficult to deal with :). For grid of nested namespaces I'm using the C++17 feature of `namespace_a::namespace_b::namespace_c {}`. But yeah, it's not pretty with all that indentation. Perhaps, with the advent of modules, we will have to get used to it. Guess we'll see.. – alexpanter May 26 '22 at 14:56