Treat two files as a single file

Hi,

I deal with a codebase which follows the following pattern:

  • A.hpp defines the type signatures.
  • A.hxx defines the templated definitions, and is included at the bottom of A.hpp. Logically, A.hpp and A.hxx together form the “header” for A.
  • A.cpp defines the non-templated definitions.

The problem is that, while using clangd, A.hxx will contain spurious errors since it’s usually compiled just by being included from A.hpp, but it doesn’t really makes sense on its own (it has no includes itself, for example).

Is there a way to specify that A.hpp and A.hxx files should be basically treated as one thing? So that I can use clangd with them.

Thanks,
Francesco

  • A.hxx depends on A.hpp, and so should include it at the top.
  • both are headers and should be include-guarded
// a.hpp
#ifndef A_HPP
#define A_HPP

template <int> void x();

#include "a.hxx" // IWYU pragma: export
#endif
// a.hxx
#ifndef A_HXX
#define A_HXX

#include "a.hpp"
template <int> void x() {}

#endif

Clangd will print a spurious header (main file cannot be recursively included) - this is a clangd bug - looks like Header-only library recursively including main file · Issue #337 · clangd/clangd · GitHub isn’t completely fix.
However it’s just one error, and all features should work.

HTH

Actually, that bug seems to be fixed in 15.0.2.

However, this requires me to modify all such files. Which might actually be feasible, but still kinda inconvenient.

I actually attempted to use the Compiler config directive to have a wrapper which automatically adds a couple of lines those files. But that config directory seems to not affect clangd, at least in my tests.

Sorry, clangd only supports standalone files, including headers.

I understand, I was trying to look for a way to preprocess those files transparently adding the right include.

Actually, while including from A.hxx does make A.hxx work, it then generates spurious errors in A.hpp.

Given

% tail -n +1 *
==> A.hpp <==
#pragma once

class A {
    int blah();
};

#include "A.hxx"

==> A.hxx <==
#pragma once

#include "A.hpp"

int A::blah() {
    return 42;
}

I get error

[{
	"resource": "/home/fmazzol/scratch/test-clangd/A.hpp",
	"owner": "_generated_diagnostic_collection_name_#0",
	"code": "redefinition",
	"severity": 8,
	"message": "In included file: redefinition of 'A'",
	"source": "clang",
	"startLineNumber": 7,
	"startColumn": 10,
	"endLineNumber": 7,
	"endColumn": 17,
	"relatedInformation": [
		{
			"startLineNumber": 3,
			"startColumn": 7,
			"endLineNumber": 3,
			"endColumn": 8,
			"message": "Error occurred here",
			"resource": "/home/fmazzol/scratch/test-clangd/A.hpp"
		},
		{
			"startLineNumber": 3,
			"startColumn": 10,
			"endLineNumber": 3,
			"endColumn": 17,
			"message": "'/home/fmazzol/scratch/test-clangd/A.hpp' included multiple times, additional include site here",
			"resource": "/data/home/fmazzol/scratch/test-clangd/A.hxx"
		}
	]
}]

Possibly clangd looks at each file in isolation, and then incorrectly derives that class A is defined twice, because they both do? Even if A.hxx would never be included anywhere else.

Actually, the problem does not seem to be specific to clangd – I’m investigating further…

FWIW your example doesn’t produce any diagnostics for me in a recent clangd.

@sam-mccall indeed, it does seem to work with 15.0.2. Thanks again for your help.