4

Let's suppose we have an existing Visual Studio 2019 solution that can't be generated from scratch with another tool, e.g. CMake.

The solution contains three projects: A, B and C, each containing only one file, this is a minimal example:

/// Project A, A.hpp
// content irrelevant

/// Project B, B.hpp
#include <A.hpp>  // B's public interface includes A's header

/// Project C.cpp
#include <B.hpp>  // C requires B's header, which includes A's header, to compile

Being used to CMake's "target-based" approach, I naively thought that I could handle things if:

  • B references A,
  • C references B

In this case however, C only consumes B's include paths, but it does not consume A's include paths. The C therefore does not compile.

The "workaround" which just "makes it work" is obvious: add A's include paths to C and it will compile. But we know that this scales horribly when number of project grows:

> /// Project C2, C2.cpp 
> #include <B.hpp>  // requires B interface to compile

> /// Project C3, C3.cpp 
> #include <B.hpp>  // requires B interface to compile

> /// Project C4, C5, ... and on and on 

Instead of having to having to specify the needs of B's interface across many projects, I am seeking what CMake offers: define the requirements (in my case: include paths) of B in B project itself and that's it.

My question is: how to achieve this entirely from within the Visual Studio 2019 IDE?

[1]: https://i.stack.imgur.com/CAMws.png

Nejc
  • 927
  • 6
  • 15
  • Do you want a CMake solution or a pure VS solution? As far as I understood you, you don't want CMake - so what has it to do with CMake at all? – Bernd Feb 21 '21 at 22:29
  • In addition linking seems to be already configured (Reference nodes in solution explorer). So basically you want to adjust the include paths for your projects, right? – Bernd Feb 21 '21 at 22:30
  • @Bernd Thank you for pointing that out, I am indeed looking for pure VS solution. I removed the cmake tag now. As for other question, yes - include paths. – Nejc Feb 22 '21 at 06:50
  • I don't believe VS has any such option. Besides, it is not correct to include all A's includes. As in general A might have some includes totally unnecessary includes for B. I manage it rather simply. I never write `#include "B.hpp"` but rather `#include "B/B.hpp"` so projects need to include but the folder all projects are located in. Or if you have multiple folders your projects are located in, you could even write it like `#include "SolName/B/B.hpp"` so the whole include is but a single folder for the entire solution (besides the third party includes...) – ALX23z Feb 22 '21 at 12:09
  • The usual 100% UI way is to define the #include the way you want, ie: "a\a.hpp" or "a.hpp", etc.., and then "play" with referencing projects properties "Configuration Properties" / "VC++ Directories" / "Include Directories" and add the relative path, ie: ".." or "..\a" or anything else. This way you never change the source code but can create an infinite variation of projects. Otherwise you can use MSBuild's .csproj to do more powerful stuff. – Simon Mourier Feb 23 '21 at 10:16
  • Select *all* projects in the Solution Explorer window (Shift+Click makes it easy). Project > Properties, note the comboboxes at the top of the dialog. Select "All Configurations" and "All Platforms". – Hans Passant Feb 23 '21 at 17:09

3 Answers3

0

You can configure the include path manully with right project click, properties, "C++ directories" enter image description here

This works but performs not really well / is not that nice. You could configure it via MSBuild e.g. with a shared props file, too. But I do not like that approach either.

In my eyes the best option to do this, not just for multiple parts (exe, dlls... ) - but for cross-compiliation, too - is the usage a shared library / project with shared elements.

This allows you to put all interface files right there. I go even a step further - my projects contain no code - it is stored completely in shared projects. With this setup you can create for example a Windows Project which uses MSVC and a Linux version compiled with GCC using the same code base. Or create a static or dynamic library - nothing is fixed.

To use code from a shared project you can add it simply as reference (as you did with your other projects) - quite easy. If you need further help - please ask.

Here you can find a sample on github: https://github.com/bernd5/Stackoverflow-VS-Sample

For help some picture (my VS is in German - but I hope you can work with it):

Create Shared Project Project-Explorer


If you dislike the shared library approach you can find an alternative approach without ".." here: First you need to adjust the include-directories property for each project to: with SolutionDir (Added MSBuild-Variable SolutionDir)

Then you can simply write:

#include "A/A.h"
Bernd
  • 2,113
  • 8
  • 22
  • BTW.: if you need 3rd party libraries like boost or openCV I would recommend to you vcpkg - it has a nice MSBuild integration – Bernd Feb 22 '21 at 22:29
  • Do you still miss something? Please leave a comment... – Bernd Mar 01 '21 at 21:31
0

According to https://learn.microsoft.com/en-us/cpp/build/adding-references-in-visual-cpp-projects?view=msvc-160#consuming-static-libraries it seems that they recommend accessing the headers using:

#include "../A/A.hpp"

Which just exploits the solution structure and continues to work.

I just tested this and it does work... I put the example at https://github.com/kyotov/ABC

Kamen
  • 47
  • 7
  • 2
    It works usually - but in my eyes the usage of ".." is a bad design and error-prone. Here you can find a thread with same topic: https://stackoverflow.com/questions/15120330/include-parent-directorys-file – Bernd Feb 23 '21 at 08:13
  • It would be better to add the solution root path to the include-path-set as described by ALX23z – Bernd Feb 23 '21 at 08:24
  • @Bernd Unless you have C++/20 and modules, this is the correct answer. I can agree that’s a bad design, but that bad design is from 1972 when C was introduced. – Soonts Feb 23 '21 at 16:27
  • 1
    Of course #include is very old mechanism - simple and hard to do correctly (mostly: include as less as possible). But that's not the point here - I wanted to say that ".." is bad style. Okay, in Visual studio it is not that bad because you usually do not create sub-folders in projects just virtual ones called "filter" but in general it should be avoided. – Bernd Feb 23 '21 at 21:45
  • 1
    Completely agree, it is just hard to avoid. One option (which is a bit better, but not ideal, and you mentioned it already) is to include the root of the solution to the include paths. This way you can skip the ".." but you will still need to be doing `#include //`. A good practice is to create a special directory in the project with public headers -- i.e. headers that you intend to be used by the customers of the project. Then make sure you only include from there... e.g. `#include "A/api/header.h"` – Kamen Feb 24 '21 at 02:43
0

As some commented, the easiest way to fix that may be to add an upper level folder as include path and then include files relatively from here.

If your code is structured like that:

your_code/
    A/
        a.hpp
        a.cpp
    B/
        b.hpp
        b.cpp
    C/
        c.hpp
        c.cpp

Then, instead of adding your_code/A in B's include path and your_code/B in C's, add your_code to eveyone's and then include A/a.hpp from B and B/b.hpp from C. I think this approach is cleaner than using .. in your includes as proposed by Kamen.

The code can be organized differently (have inc/src folders to isolate headers/implementations, repeat library name under inc folder so that people having any_lib's inc in their library path would include any_lib/...hpp to avoid conflicts in header files lookup...), but in the end, the approach remains the same, try to have B lookup for A's include relatively from a place also known by C so that it finds it too.

Anyway, the good solution (long term) may be to use CMake in the end (or any build system) handling that correctly for you, but that is apparently not an option right now.


Edit: This post shows how to add a bunch of includes to a projet. You may also use that, have a IncludeDirsForB.txt with everything needed by B path and add @IncludeDirsForB.txt to the command line of every project using B...?

jpo38
  • 20,821
  • 10
  • 70
  • 151