-2

It's been considered for a reason, that using for namespaces/names is side-effect-prone, and generally, fully qualified names should be preferred.

I've come up with an approach on that, which I couldn't find implemented yet, so I created my own project.

Briefly, the idea is to define a set of macro which would unfold into the following structure:

namespace your_namespace {
    namespace __local__ {
            //includes and other non-exported names go here
        namespace __exported__ {
            //actual code goes here
        }
    }
    using namespace __local__::__exported__;
}

It isolates the contents of __local__ from your_namespace, while keeping them visible to it's logically-actual part.

General usage looks like this:

#include <namespace_util.hpp>

NAMESPACE(foo)
//here go using declarations, and the names you don't want to be visible from outside
NAMESPACE_EXPORTS
//here go your namespace members
NAMESPACE_END

The implementation:

#define NAMESPACE(name)\
    namespace name {\
        namespace __local__ {\

#define NAMESPACE_EXPORTS\
            namespace __exported__ {

#define NAMESPACE_END\
            }\
        }\
        using namespace __local__::__exported__;\
    }

Since I'm relatively new to C++, I can't be completely sure about the solution, so I ask the people: please see the (much more detailed) README, the code, and give your feedback.

Thank you.

  • So you are "relatively new to C++" and come up with an approach that you "couldn't find implemented yet." Sounds like this should be on codereview. – Sjoerd Apr 19 '17 at 20:47
  • They kicked me in here :) – oleg.lukyrych Apr 19 '17 at 22:09
  • 2
    "using for namespaces/names is side-effect-prone" What is side-effect-prone supposed to mean? using just allows you to refer to things without the qualified name, it's perfectly usable in individual translation units, and you can minimize issues by using specific functions/classes/etc. rather than the whole namespace (using namespace does basically defeat the purpose of namespaces, if that's what you meant). In fact, if you want to do proper ADL with std::swap you need using std::swap inside your swap implementations. – JAB Apr 20 '17 at 16:19
  • >>What is side-effect-prone supposed to mean?>> this have been answered by me across the comments. I will update the post or make a self-answer with the summary soon. – oleg.lukyrych Apr 20 '17 at 16:59
10

Your approach is not technically wrong (except a minor detail, see footnote) but it goes against established usage patterns and obfuscates code. I.e. it lowers readability and increases the likelihood of errors. That might be justified if it is outweighed by a clear and significant advantage somewhere else. And here lies the problem. Sjoerd already pointed it out in his answer, but let me reiterate: Precicely what is the problem you are trying to solve? Why is your solution significantly better than the established one? An advantage is not apparent at all, so considering the drawbacks this approach is not useful.

The most severe problem I see are the NAMESPACE macros. They replace basic structural C++ elements, essentially creating a language inside the language. The structure of namespaces and symbol names inside is a central part of the API a header exposes. Obfuscating that structure makes it much harder to understand what’s going on in that header.

Looking at the pattern itself, I’m still not convinced. You’ve now made it clearer in some comments and your updated question that you want to be able to use names from the __local__ namespace without qualifying their names and without the transitive effects of using namespace. On the one hand that can indeed be a benefit for readability, especially if it avoids awkward line breaks because of long qualified names. On the other hand a specific using or using namespace inside a function has the same effect where it’s really needed. Also declaring your external symbols in a namespace that is a child of an internal namespace and then pulling them out of there again with a using is not very elegant. All in all I find the pattern not convincing, but mostly harmless (not regarding the macros!).

For comparison: This is the idiomatic pattern to hide private symbols, i.e. a developer familiar with the C++ ecosystem will expect to see it and intuitively know what it means:

// some_file.cpp
namespace my_ns {
    namespace {
        // Most private symbols go here.
        const int foo = 1;
    }

    int double_foo() {
        return foo * 2;
    }
}


// some_file.hpp
namespace my_ns {
    namespace detail {
        // Private names that cannot be hidden in the .cpp
        // go here. Other common names for this namespace are:
        // "impl", "implementation", sth with "private"
        const int bar = 2;
    }

    int double_foo();

    // function that cannot be implemented in the .cpp
    template <typename T>
    void add_bar(const T& to) {
        using namespace detail;
        // or use the qualified name instead
        to += bar;
    }
}

Footnote: Minor technical detail: A lot of underscored names are reserved for the implementation (of the standard). See the standard, section [reserved.names] for full details. In your code the double-underscored names are problematic. The be-on-the-safe-side rule is: Do not create your own names starting with an underscore and avoid double underscores altogether.

  • 1) There exists the transitive effect of using (for names). In my opinion, it can be unwanted in some situations, but there's no clear way to disapply it. That's the problem I'm trying to solve. So, I proposed a pattern (no macro at this point) for that purpose. The pattern itself, in a way, relies on semantic encapsulation (underscored prefix/suffix), a common practice to discourage the unintended usage. In addition, I made a couple of macros to simplify the pattern usage. – oleg.lukyrych Apr 20 '17 at 12:34
  • I'm perfectly aware of flaws of macro and underscore encapsulation, yet I think it's the reasonable price for ability to safely use using at namespace level + hide the names within a namespace. – oleg.lukyrych Apr 20 '17 at 12:34
  • 2) In the following excerpt foo actually will have been put into my_ns. Anyone can access it like my_ns::foo. That's a strange meaning of "private". ``` namespace my_ns { namespace { // Most private symbols go here. const int foo = 1; } int double_foo() { return foo * 2; } } ``` – oleg.lukyrych Apr 20 '17 at 12:34
  • 3) You're right on reserved underscored names. I intentionally used them for the sake of demonstration. They should be changed. – oleg.lukyrych Apr 20 '17 at 12:34
  • 2
    Using the macro distracted attention away from the underlying technique, for the reasons I mentioned in my answer. In general, if you want to ask about some code style, hiding it behind a macro is a bad approach, because then people have to pre-process your code in their heads before they can think about it. – Useless Apr 20 '17 at 12:48
8

Looks like a wrong approach, for several reasons:

  1. The things that you don't want to be "visible" to outside, should not go into headers. Instead they go into anonymous namespace in the source file.
  2. Your __local__ things are still visible, and everyone can access them
  • 1. Debatable. What If I design a header-only library, and for the sake of modularity and brevity want to introduce a namespace tree, and import names to use short id's, without being afraid of side-effects? – oleg.lukyrych Apr 19 '17 at 16:55
  • 2. That's addressed in README. It's discouraged to use internal namespaces directly. I can't fully restrict it, but common sense should imply not to use something named _______do_not_touch_me______ – oleg.lukyrych Apr 19 '17 at 16:57
  • @oleg.lukyrych Header only (I guess something like boost) can be done like you explained: things that should not be used is put into such ("implementation") namespace. For normal code, I would avoid it. Regarding the 2nd comment, don't expect people to read README files or comments - they want to use your library without that. – BЈовић Apr 19 '17 at 16:58
  • 3
    @oleg.lukyrych You are "relatively new to C++." My advice: Accept advice when given, and stop arguing everything. – Sjoerd Apr 19 '17 at 20:49
  • 1
    The "relatively new to C++" is a direct quote from your post. And you're arguing again - proving my point. – Sjoerd Apr 19 '17 at 21:06
6

What problem do you want to solve? That is completely unclear.

As for your code, if you were a junior assigned to me, I would say:

  • Your approach doesn't hide implementation details at all - they are still visible in the header.

  • Your approach hides things behind C-style macros, which are considered "avoid whenever possible" by the C++ community. So replacing a solution not using macros with a solution using macros, is going in the wrong direction.

  • You state

    using for namespaces/names is side-effect-prone

    but write using namespace yourself. So you're inconsistent.

After pointing out those basic mistakes, I wouldn't spend any more time on your code. Instead, I would go back to the main question: What problem do you want to solve?

  • 4
    @oleg.lukyrych Four comments by you later, and there is still no clear answer to the question "What problem do you want to solve?" If the problem is the supposed "side effect problem" (the transitive effect of using): that's by design, and is not considered a problem at all by the C++ community. – Sjoerd Apr 19 '17 at 21:52
  • 3
    Well, apparently the C++ community uses all those "endless snakes of fully qualified names". You're the only one who thinks it's a problem. – Sjoerd Apr 19 '17 at 22:02
  • 3
    If you want shorter names, use using sh_nm = long::inner::very_long_name; Or typedef the long name. – Sjoerd Apr 19 '17 at 22:06
  • 4
    It's not a problem. Get over it. That's my last advice here. You clearly are not listening to advice - you just want to be confirmed in your believes. – Sjoerd Apr 19 '17 at 22:07
  • 1
    Besides the macro, it's still adding complexity. Since you've consistently refused to provide an example of any code which warrants this complexity, most people are going to say there's no benefit, or insufficient benefit to justify that complexity. Once again, if you do have some minimal code that demonstrates the problem, post that. You're asking for a judgement call (does A justify B) and not showing A. You could ask "could any A justify B", but that's not an interesting question. You actually asked "do you think there will often arise As which justify B", which is opinion. – Useless Apr 20 '17 at 12:52
4

If you really want constructive feedback on this approach, it would have been better to show some actual code that you think benefits from it. Then we'd have something concrete to discuss rather than hypotheticals.

Since there's something like a usage example in your linked README, that should have been in the question: it's helpful to see, and putting essential parts of the question in external links works badly. I didn't even notice the link was there until after I'd read both the question and all the existing answers, and link rot is also a concern.

Now, looking at your sample usage:

#ifndef MYPROJECT_MYCLASS_HPP
#define MYPROJECT_MYCLASS_HPP

#include <namespace_util.hpp>

INLINE_NAMESPACE(myproject, MYPROJECT_MYCLASS_HPP)

using namespace std;

NAMESPACE_EXPORTS

class MyClass {

};

NAMESPACE_END

#endif

My first thoughts, approaching this without warning, are:

  1. What on earth is this?
  2. Oh good, someone else has written a new sub-language in preprocessor. At least it isn't a horribly broken FOREACH macro this time.
  3. I bet they've written a FOREACH somewhere as well, I'll have to keep an eye out for that.
  4. grep -r '#define private public' ... no matches, what a relief
  5. It's impossible to tell at a glance how to use your class - unless I'm already familiar with your macro idiom, it makes me work harder to figure out it should be called myproject::MyClass
  6. this doesn't seem to save much time or effort over just writing it out directly, with explanatory comments.

    That is, the macro-free version below isn't much more verbose, is no less clear (they're both confusing the first time you see them, and once you're used to the idiom anyway, the macros are no clearer)

Macro-free version:

#ifndef MYPROJECT_MYCLASS_HPP
#define MYPROJECT_MYCLASS_HPP

namespace myproject {
namespace myclass_detail { // external dependencies go here
  using namespace std;     // scope limited to myclass_extern
  namespace publ {         // public declarations in here
    class MyClass {};
  }
}
using namespace myclass_detail::publ;
}

#endif
  • Thank you for an attempt to understand. Unfortunately, my thoughts have dispersed over the comments to the answers, but generally, yeah, macro-free version can be used, why not? I made macros as a supplement to the idea. – oleg.lukyrych Apr 20 '17 at 12:50
  • 4
    Never first express your idea using macros - people get hung up on the macro part, and they obscure the code rather than helping. There are lots of reasons to avoid them, and the few exceptions require better justification than "I thought some names looked long and ugly, but I won't show you those names or any code" – Useless Apr 20 '17 at 12:56
1

The purpose of namespaces in C++ is to avoid name clashes between code from different (unrelated) libraries. E.g. libA can define a class or function called libA::foo without clashing with libB::foo. What's more, version 2 of libA can introduce a name that is already used in libB. The code in libB, as well as other code that uses both libA and libB, will continue to compile and behave the same when we upgrade libA from version 1 to version 2.

When you introduce using namespace ... in your code, you lose the protection offered by (proper use of) namespaces. When you upgrade a library whose namespace you imported, your code may fail to compile, due to name clashes. Or (worse), your code may still compile, but behave differently, e.g. because a different function overload is selected during overload resolution.

This problem is not solved by your proposed namespace scheme. When libB depends on libA and imports its namespace, upgrading libA may cause libB to fail to compile (or behave incorrectly).

So, your proposed scheme for the use of namespaces serves a purpose only where the release of dependent libraries are managed collectively. I.e. libB is checked to compile and behave the same whenever libA is modified and/or libB is updated to avoid errors whenever libA is modified. However, in that case it is questionable why these libraries use different namespaces in the first place.

  • 1) Quote from you: "The purpose of namespaces in C++ is to avoid name clashes". And that's it. The library stuff you mentioned is the application, which is not the only one. – oleg.lukyrych Apr 21 '17 at 10:56
  • 2) I'm not talking about specifically using namespace <name>. As for your example, using <name> should be just fine. – oleg.lukyrych Apr 21 '17 at 10:56
1

I'd like to share my own thoughts to this problem, as I've been toying with this same idea myself.

Your solution using namespaces works in headers, but it obfuscates compiler error messages. An alternative solution would be this:

// header

namespace Internal {
    namespace ProjectNamespace {

        // using declarations, helpers, etc.
        class A { ... };
    }
}

namespace ProjectNamespace {

    using A = Internal::ProjectNamespace::A;
}

With this, two things happen: only one unexpected namespace is shown to the user, Internal, and isn't super weird looking like __local__ or __exports__. Secondly, the namespaces after Internal actually reflect the structure of the API, so the user won't be confused by seeing __export__ as the enclosing namespace, where the user typically gets the most context around whatever they're using.

It may use a using declaration, but typically you only design one function/class per file, so it will be the same number of lines (1) in many situations. It's worth it IMO to get rid of the second unexpected namespace name.

-1

Upon getting some extent of feedback, I feel like it's not much more of it to expect, so I'd like to summarize all the said.

A portion of critique addressed that the problem I'm trying to solve isn't clear.

There exists the transitive effect of using namespace/name. In my opinion, it can be unwanted in some situations, but there's no standard way to disapply it. That's the problem I'm trying to solve. So, I proposed a pattern for that purpose:

namespace your_namespace {
    namespace __local__ {
        //names not intended to be in your_namespace, including those introduced by "using" directive should be here
        //they will be visible to the de facto body of your_namespace, while not being exposed
        namespace __exported__ { //will be inlined into your_namespace
            //everything you'd like to have in your_namespace
        }
    }
    using namespace __local__::__exported__; //the inlining
}

The topicality is questionable.

While the classic practices of name handling do work, they might imply verbosity, and force some certain design. In my opinion, the pattern is reasonably simple, harmless, and grants freedom in the sense that it makes some coding styles not error-ish. It was fairly noticed that the applications aren't clear. I'd break them into the following categories:

  1. Boilerplate reduction. The main source of verbosity is complex names -- nodes of the name tree with depth > 1, where 0 is the global itself. They may come from your own code, and they often come from external code you want to reuse. In the former, it's not a problem as long you don't have a complicated namespace tree. In the latter, it doesn't depend on you, and might potentially be quite significant. Imagine a library you need to use, which heavily uses namespaces (justified by complexity), like Boost. You may end up with code like:

    //library.hpp
    namespace library {
        namespace foo {
            struct A;
            namespace bar {
                struct B;
                namespace baz {
                    struct C;
                    struct D;
                    //...
                }
            }
        }
        //... Hypothetically endlessly complicated name tree
    }
    //module1.hpp
    //includes library.hpp
    namespace myproject {
        // can't use using directive, because it may cause name conflicts in other modules,
        // and in the namespaces using "myproject" namespace
        library::foo::A someFn(library::foo::bar::B, library::foo::bar::baz::C, library::foo::bar::baz::D);
        library::foo::bar::baz::C someOtherFn(library::foo::bar::baz::C, library::foo::bar::baz::C, library::foo::bar::baz::D);
        struct E {
            library::foo::A a;
            library::foo::bar::B b;
            library::foo::bar::baz::C c;
            library::foo::bar::baz::D d;
            //...
        };
        //... Hypothetically endless number of inevitable endlessly complex and varying names usages
        //... Hypothetically endlessly complicated name tree
    }
    //module2.hpp
    //includes some other name-complex libraries
    namespace myproject {
        //... Hypothetically endless number of differently named members (not conflicting with existing actual members)
        //... Hypothetically endless number of inevitable endlessly complex and varying names usages
    }
    

    It's greatly simplified with the template:

    //module1.hpp
    //includes library.hpp
    namespace myproject {
        namespace module_1 {
            using namespace library::foo; //these directives won't impact "myproject"
            using namespace bar;
            using namespace baz;
            namespace __exported__ {
                A someFn(B, C, D); //users of the function would have to explicitly deal with external names, which is right in my vision
                C someOtherFn(C, C, D);
                struct E {
                    A a;
                    B b;
                    C c;
                    D d;
                };
            }
        }
        using namespace module_1::__exported__;
    } 
    

    That was an example of one-namespace-project. It's even more actual when there's a namespace per module, and modules depend on each other. That also was an example of classic headers-with-declarations-sources-with-definitions. When you design a header-only library, then there's an additional burden of handling long names e.g. in function bodies.
    Despite that the workarounds exists, they've got flaws:

    • Typedefs are not applicable to all kinds of names, and itself introduce a name in the namespace

    • Namespace alias is still not a short name, and it's itself a name in the namespace.

    • Using directives at function scope will likely duplicate, because different functions often work with same names.

  2. Encapsulation. This approach allows not only to isolate imports, but also to have namespace-private members.

  3. Design rationalization. Nobody will question the virtue of modularity. While there are mechanisms to implement modularity of code, namely, different files and inclusion, I don't think it's enough. The names are in the essence of a module -- it's logical structure. C++ allows for the physical name of a module (filename), and the logical name to diverge, but I think they should be consistent. And the names in the module should not be mixed with the names it depends on. I will regret saying it, but in other module systems, e.g. ES6 or Java, modules have complex names for better identification e.g. packageName.shortName, which are consistent with their physical names; they have inner names, which they export, and the names they consume (import), and the latter never conflicts with the former; I personally adopt these views, and don't see how this concept conflicts with C++.

Usage of macros is discouraged.

I created macros as a supplement to the idea. The pattern can be used without them. Creating them I was biased to the individual perception, where it was familiar, and considered more declarative & code-clean. As long as this thing is not standardized within a group, it should be, of course, avoided by members of the group.

Insufficient encapsulation.

Direct usage of your_namespace::__local__ would screw everything up. That's why I obscured it via underscored name. This encapsulation technique is purely semantic, thus not fully reliable: anyone can still refer to that name. Unfortunately, it's the best you get here. Anyway, I think the common sense hints not to use something named like __do_not_touch_me__.

Usage of underscored names is discouraged.

Yeah, it violates the recommendations of the standard, and should be changed.

I thank everyone for participating!

protected by gnat Apr 12 at 7:38

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.