2

I have a namespace, N0, that has sub-namespaces including N1. The calling code only knows about the outer namespace. I'd like to write a function in the outer namespace that returns a std::unique_ptr<N1::T> where that result is consumed elsewhere in N0. However, the caller shouldn't know about N1. What I'd like to do is something like:

// N0.h
namespace N0 {
    typename T; // This isn't real C++.
    std::unique_ptr<T> foo();
    void bar(std::unique_ptr<T>&&);
}
// N0.cpp
#include "N1.h" // Get N1::T
namespace N0 {
    typedef N1::T T;
    ...
}

That is, I'd like to expose a type that the caller can't see but internally I'd like to actually use a type in a different namespace. This way elsewhere someone could just forward-declare namespace N0 { class T; } without having to know that T is actually in N1.

I could move T itself into N0, but it really belongs in N1.

I could wrap T with a dummy class in N0, but that's ugly, and the pointer should basically do that.

I could probably make a class N0::T that subclasses N1::T, but that seems icky too.

Is there no way for N0 to forward declare that "I have a type and you don't need to know what it is" and have that type actually be in a different namespace? Put another way: Why is class C; class C{}; legal but class C; typedef int C; is illegal? (Likewise class C; using C = int; or typedef C; typedef int C;.) They seem fundamentally the same to me and I can't think of a clever template trick to get around it. The only difference I can think of is that the typedef version wouldn't be subject to Koenig lookup.

Ben
  • 9,184
  • 1
  • 43
  • 56
  • I don't think so. I just want `N0` to be able to publicly say "I have a type `T`" that you can have pointers to and then privately have `N0` define it as "actually when I say `T` I mean `N1::T`. – Ben Mar 05 '18 at 17:25
  • 1
    Inheritance might be an option https://stackoverflow.com/a/44136249/597607 – Bo Persson Mar 05 '18 at 18:38

3 Answers3

0

I mean you could do this:

// N0.h
namespace N0 {
    std::unique_ptr<T> foo();
    void bar(std::unique_ptr<T>&&);
}
// N0.cpp
namespace N0 {
    typedef N1::T t;
}

#include "N0.h"

namespace N0 {
    // whatever...
}
Rakete1111
  • 47,013
  • 16
  • 123
  • 162
  • I don't follow. Where does `T` come from in `N0.h`? – Ben Mar 05 '18 at 17:41
  • @Ben From nowhere. Well, the trick here is to include it *after* already defining `N0::T`, so that the code inside the header becomes valid. It's basically something [like this](https://godbolt.org/g/R4wkuo), where the middle namespace is what is in your header. – Rakete1111 Mar 05 '18 at 17:43
  • But what of the caller of `foo()`? They'll `#include "N0.h"` and get ` undeclared identifier 'T'` right? – Ben Mar 05 '18 at 20:27
  • @Ben Jup if you don't specify which `T` you want to use, and you can only use it for a single `T` – Rakete1111 Mar 05 '18 at 20:29
0

In the situation you have described, the foo should be implemented as a template function:

namespace N0 {
    template <typename T>
    std::unique_ptr<T> foo(){...};

    template <typename T>
    void bar(std::unique_ptr<T>&&){...};
}

And you should using a wrap/overload function to do the final trick:

namespace N0 {
std::unique_ptr<N1::T> foo() { return foo<N1::T>(); }
//for bar there is no need to wrap, cause the T could be resolved by parameters.
}
whitebob
  • 48
  • 6
0

Here's the best I've come up with, which seems to work. I still feel like there should be a way to not use "tricks" to make N1::T fully hidden from callers:

// N0.h
#pragma once
#include <memory>

namespace N0 {
    struct OpaqueObject { virtual ~OpaqueObject() {} };
    std::unique_ptr<OpaqueObject> foo();
    void bar(std::unique_ptr<OpaqueObject>&&);
}
//N0.cpp
#include "N1.h"

namespace N0 {
   std::unique_ptr<OpaqueObject> foo() { return std::unique_ptr<N1::T>(new N1::T()); }
   void bar(std::unique_ptr<OpaqueObject> &&) {}
}
// N1.h
#pragma once
#include "N0.h"

namespace N1 {
  class T : public N0::OpaqueObject {};
}
// test.cpp
#include "N0.h"

int main() {
  auto x = N0::foo();
  N0::bar(std::move(x));
}
Ben
  • 9,184
  • 1
  • 43
  • 56