67267
環境は次の通りです。

  * macOS Catalina 10.15.4
  * g++ (Homebrew GCC 9.3.0_1) 9.3.0
  * C++ 17
  * Bazel 3.1.0

問題に遭遇しているのは、  
ある二つのプロジェクトaとbがあり、bがaに依存しているような場合です。

解決したいエラーとは以下のものです。

```

    $ bazel build --cxxopt -std=c++17 //src:b
    
    src/b.cc:11:18: error: no type named 'Exception' in namespace 'a'; did you mean simply 'Exception'?
      } catch (const a::Exception& e) {
                     ^~~~~~~~~~~~
                     Exception
    ./src/exceptions.h:5:7: note: 'Exception' declared here
    class Exception {};
    
```

```

    $ g++ -std=c++17 -Wall --pedantic-errors -I . -I bazel-b -I bazel-b/external/a -c src/b.cc bazel-b/external/a/src/a.cc
    
    bazel-b/external/a/src/a.cc: In member function 'void a::A::DoA() const':
    bazel-b/external/a/src/a.cc:6:29: error: 'Exception' was not declared in this scope; did you mean 'b::Exception'?
        6 | void A::DoA() const { throw Exception(); }
          |                             ^~~~~~~~~
          |                             b::Exception
    In file included from bazel-b/external/a/src/a.h:4,
                     from bazel-b/external/a/src/a.cc:1:
    ./src/exceptions.h:5:7: note: 'b::Exception' declared here
        5 | class Exception {};
          |       ^~~~~~~~~
    
```

Bazelでは、プロジェクトb側でaの`Exception`を使用しようとするとエラーになっています。  
g++では、a側の`Exception`にエラーがでてます。

各プロジェクトのディレクトリ構造は以下のようになっています。

  * プロジェクトa

```

    .
    ├── WORKSPACE
    └── src
        ├── BUILD
        ├── a.cc
        ├── a.h
        └── exceptions.h
    
```

  * プロジェクトb (Bazelが生成するファイルは関係がありそうなファイルのみを表示しています）

```

    .
    ├── WORKSPACE
    ├── bazel-b/external/a // プロジェクトa
    └── src
        ├── BUILD
        ├── b.cc
        ├── b.h
        └── exceptions.h
    
```

各ファイルの内容は次の通りです。

  * a/WORKSPACE (空)

```

    
```

  * a/src/BUILD

```

    load("@rules_cc//cc:defs.bzl", "cc_library")
    
    cc_library(
        name = "a",
        srcs = ["a.cc"],
        hdrs = ["a.h"],
        visibility = [
            "//visibility:public",
        ],
        deps = [
            "//src:exceptions",
        ],
    )
    
    cc_library(
        name = "exceptions",
        hdrs = ["exceptions.h"],
    )
    
```

  * a/src/a.h

```

    #ifndef A_A_H
    #define A_A_H
    
    #include "src/exceptions.h"
    
    namespace a {
    class A {
     public:
      void DoA() const;
    };
    }  // namespace a
    
    #endif
    
```

  * a/src/a.cc

```

    #include "src/a.h"
    
    #include "src/exceptions.h"
    
    namespace a {
    void A::DoA() const { throw Exception(); }
    }  // namespace a
    
```

  * a/src/exceptions.h

```

    #ifndef A_EXCEPTIONS_H
    #define A_EXCEPTIONS_H
    
    namespace a {
    class Exception {};
    }  // namespace a
    
    #endif
    
```

  * b/WORKSPACE

```

    local_repository(
        name = "a",
        path = "../a",
    )
    
```

  * b/src/BUILD

```

    load("@rules_cc//cc:defs.bzl", "cc_library")
    
    cc_library(
        name = "b",
        srcs = ["b.cc"],
        hdrs = ["b.h"],
        deps = [
            "//src:exceptions",
            "@a//src:a",
        ],
    )
    
    cc_library(
        name = "exceptions",
        hdrs = ["exceptions.h"],
    )
    
    
```

  * b/src/b.h

```

    #ifndef B_B_H
    #define B_B_H
    
    namespace b {
    class B {
     public:
      void DoB() const;
    };
    }  // namespace b
    
    #endif
    
```

  * b/src/b.cc

```

    #include "src/b.h"
    
    #include "external/a/src/a.h"
    #include "src/exceptions.h"
    
    namespace b {
    void B::DoB() const {
      try {
        auto a = a::A();
        a.DoA();
      } catch (const a::Exception& e) {
        throw Exception();
      }
    }
    }  // namespace b
    
```

  * b/src/exceptions.h

```

    #ifndef B_EXCEPTIONS_H
    #define B_EXCEPTIONS_H
    
    namespace b {
    class Exception {};
    }  // namespace b
    
    #endif
    
```

実は、a側の`Exception`の定義を`a/src/a.h`に移動させると、このエラーは解決されました。  
（#includeはコードをその場に展開するものだと思うのですが、この違いがわかっていません。）

ただベストとしては、a側のコードを変更しないこのエラーの解決法があればと思っています。
CC BY-SA 4.0
2020-06-03T14:58:21.343
67262
2020-06-03T17:07:47.640
2020-06-03T15:06:51.203
40468
40468
question
c++
同じ名前の2つのクラスをそれぞれ別々の名前空間に定義しているが、片方のみに解決しようとする
Bazelは使ったことがないので、エラーの原因の説明だけになります。

プロジェクトbをビルドするとき、カレントディレクトリはプロジェクトbになっています。`b/src/b.cc`をコンパイル中に`#include
"external/a/src/a.h"`を読み込み、さらにその中の`#include
"src/exceptions.h"`を読み込もうとしますが、この時カレントディレクトリがbなので、プロジェクトbの`src/exceptions.h`を読み込んでいます。つまり、プロジェクトaの`src/exceptions.h`は読み込まれません。

`b/src/b.cc`のコンパイルエラーを消すだけなら、`#include
"external/a/src/exceptions.h"`を`#include
"external/a/src/a.h"`の前に置けば、うまくいくと思います。

```

    #include "src/b.h"
    
    #include "external/a/src/exceptions.h" // <- ここ
    #include "external/a/src/a.h"
    #include "src/exceptions.h"
    
    namespace b {
    void B::DoB() const {
      try {
        auto a = a::A();
        a.DoA();
      } catch (const a::Exception& e) {
        throw Exception();
      }
    }
    }  // namespace b
    
```

プロジェクトaも一緒にビルドしようとしているようですが、そうするとプロジェクトaのコンパイル時にも、同様なエラーが起きると思います。一般的には、aをコンパイルするときに一時的にカレントディレクトリを変更するなどすると思いますが、Bazelを使ったことがないので具体的な方法はわかりません。
CC BY-SA 4.0
2020-06-03T16:44:29.173
67265
2020-06-03T16:44:29.173
3605
67262
answer
`#include "src/exceptions.h"` が指すファイルが2つあり、プロジェクトa内の `#include
"src/exceptions.h"` がプロジェクトbの `src/exceptions.h` を読み込んでしまっているようです。

`#include ""` は次の順序でファイルを検索します。

  1. `#include`が書いてあるファイルのディレクトリ
  2. コマンドライン (`-I`) や環境変数で指定したディレクトリ
  3. システムヘッダのディレクトリ

プロジェクトBをビルドするとき、プロジェクトBのトップディレクトリがコマンドラインで指定されているかと思います。この状態でプロジェクトB内のファイルが
`external/a/src/a.h` をインクルードすると、`a.h` 内の `#include "src/exceptions.h"`
はプロジェクトBのトップディレクトリ相対で見つかります。

### 解決策1

`a.h` や `a.cc` 内で `exceptions.h` を相対パスでインクルードすると問題を回避できます。

```

    #include "exceptions.h"
    
```

### 解決策2

プロジェクトaの `src/exceptions.h` を `src/a_exceptions.h` などに改名しましょう。

### 解決策3

プロジェクトaの`src/`ディレクトリを `a/` に変更するか、`src/` 内のすべてのファイルを `src/a/`
に移動して、プロジェクトa内の`#include`をすべて `#include "a/` で始めるようにします。

名前空間に属するソースをその名前空間のディレクトリに入れているオープンソースプロジェクトをいくつか見たことがあります。
CC BY-SA 4.0
2020-06-03T17:07:47.640
67267
2020-06-03T17:07:47.640
3475
67262
answer