Scoped using-directive within a struct/class declaration? [duplicate]
Asked Answered
M

5

29

I find that my C++ header files are quite hard to read (and really tedious to type) with all the fully-qualified types (which goes as deep as 4 nested namespaces). This is the question (all the answers give messy alternatives to implementing it, but that's not the question): Is there a strong reason against introducing scoped using-directive in structs and classes in the C++ language (while it's permissible to have scoped using-declaration in functions)?

e.g.

class Foo : public Bar
{
    using namespace System;
    using namespace System::Network;
    using namespace System::Network::Win32::Sockets;
    using Bar::MemberFunc; // no conflict with this

    // e.g. of how messy my header files are without scoped using-directive
    void FooBar(System::Network::Win32::Sockets::Handle handle, System::Network::Win32::Sockets::Error& error /*, more fully-qualified param declarations... */);
};

Since namespace is a keyword, I would've thought it's distinct enough to cause no conflict with the scoped using declaration such as Bar::MemberFunc.

EDIT: Read the question carefully ---> I've bolded it. Reminder: we're not discussing how to improve readability of the example here. Suggesting how scoped using-directive could be implemented (i.e. by means of adding keywords / constructs etc.) in the C++ language is NOT an answer (if you could find an elegant way to implement this using existing C++ language standards, then it would of course be an answer)!

Mendy answered 6/12, 2010 at 3:3 Comment(9)
Personally, I think it's a sign of bad design when there are so many nested namespaces.Variolite
By your reasoning, the .NET framework would be a bad design.Mendy
@Zach Saw - It's bad C++ design, it's not-so-great Java design and it's OK (but still not fantastic) Python design. And if the .NET framework for C++ does that, then yes, it's not very well designed. I do think though that C++ could use a mechanism like you suggested for restricting the scope of a using declaration. That would cause heavily nested namespaces to be a less bad design in C++ than they currently are.Variolite
@Omnifarious: You can compare the STL to the more extensive libraries like .NET and Java libs. If the C++ standard lib was more extensive we would definitely need to break it up into more namespaces (and of course spend 5 years coming to compromise over what it is). But we would definitely need some form of nested namespaces. Personally I think it is a good idea but you must be careful.Leban
@Martin York - I don't think nested namespaces are an evil that should never be visited upon the world. Boost uses them a lot. I just think they should be used sparingly and your nesting depth should be kept small and a given namespace should have a lot of names. The way C++ currently works makes extensively nested namespaces cause problems just like the one the OP is having.Variolite
Guys, the question is, "Is there a strong reason not to have this feature in the language?"Mendy
@Omnifarious: So what's the reason then /not/ to introduce this feature to the language?Mendy
"Suggesting how scoped using-directive could be implemented in the C++ language is NOT an answer!" <- but it is. If I can show that it can be implemented in C++ now, then that is a very strong reason for not explicitly adding such a feature to the language.Writeup
Perhaps that statement is misleading. But I meant implementing the feature on top of the existing language (i.e. by introducing new constructs / keywords etc.). If you could show that it can be implemented in C++ now (now being the operative keyword), then of course it's an answer.Mendy
V
12

Given that using declarations at class scope are not inherited, this could work. The name would only be valid inside that class declaration, or inside the declarations of nested classes. But I think it's sort of overloading the concept of a class with an idea that should be larger.

In Java and Python individual files are treated in a special way. You can have import declarations that inject names from other namespaces into the file. These names will (well, not exactly with Python, but it's too complicated to explain here) only be visible within that file.

To me that argues for this sort of ability not being tied to a class declaration, but given a scope of its own instead. This would allow injected names to be used in several class declarations if it made sense, or even in function definitions.

Here is an idea I prefer because it allows these things while still giving you the benefits of a class level using declaration:

using {
   // A 'using' block is a sort of way to fence names in.  The only names
   // that escape the confines of a using block are names that are not
   // aliases for other things, not even for things that don't have names
   // of their own.  These are things like the declarations for new
   // classes, enums, structs, global functions or global variables.
   // New, non-alias names will be treated as if they were declared in
   // the scope in which the 'using' block appeared.

   using namespace ::std;
   using ::mynamespace::mytype_t;
   namespace mn = ::mynamespace;
   using ::mynamespace::myfunc;

   class AClass {
     public:
      AClass(const string &st, mytype_t me) : st_(st), me_(me) {
         myfunc(&me_);
      }

     private:
      const string st_;
      mn::mytype_t me_;
   };
// The effects of all typedefs, using declarations, and namespace
// aliases that were introduced at the level of this block go away
// here.  typedefs and using declarations inside of nested classes
// or namespace declarations do not go away.
} // end using.

// Legal because AClass is treated as having been declared in this
// scope.
AClass a("Fred", ::mynamespace::mytype_t(5));

// Not legal, alias mn no longer exists.
AClass b("Fred", mn::mytype_t);

// Not legal, the unqualified name myfunc no longer exists.
AClass c("Fred", myfunc(::mynamespace::mytype_t(5));

This is analogous to declaring a block for local variables in a function. But in this case you are declaring a very limited scope in which you will be changing the name lookup rules.

Variolite answered 6/12, 2010 at 5:24 Comment(26)
Again, the question is, "Is there a strong reason against introducing scoped using-directive in structs and classes in the C++ language?"Mendy
@Zach Saw - I fixed my answer to actually answer your question. :-)Variolite
No it won't be the same as my idea. Derived classes won't inherit the using-directive - it's scoped within the class only. So no problem with the ambiguities as per your typedef.Mendy
@Zach Saw - Hmmm... OK, so that problem doesn't exist because you're right, other kinds of using declarations are not inherited. I still like my using scope idea better because I think it's more flexible and clearer.Variolite
@Omnifarious: Again, that doesn't answer the question. You're arguing that we should have the feature in the language -- the question is, why should we not have it.Mendy
@Zach Saw - My argument is that a different, related feature would actually be better and clearer.Variolite
@Omni: That's not a feature yet. And we're not discussing how it should look like - we're discussing why shouldn't we have it.Mendy
@Zach Saw - And I'm saying we shouldn't have it in the form you're talking about because it's too limited in scope (pun intended) that way.Variolite
@Omni: That's just an example - to help with the understanding of what I meant by "scoped using-directive within a struct/class declaration". Unless you can find a portable C++ language feature that fulfills this criteria in a more elegant manner, I can't accept your answer.Mendy
@Omni: Personally, I prefer it to be limited in scope. But that's not what this question is all about. I suppose it would make a great topic for another day if we've decided that there's no reason why C++ shouldn't support this feature to begin with.Mendy
@Zach Saw - Personally, I think that the existence of an idea for a more general and/or clearer feature is a perfectly good reason to be against the more limited and less clear feature.Variolite
@Omni: It's your idea of scope-restricted using-directive vs mine. Again, that's a topic for another day but right now, let's establish if there's a strong reason against introducing it to the C++ language.Mendy
Usually, the standards committee works the opposite way. Is there a strong reason for including it in the language? C++ is already a huge language. Every new feature really has to justify the cost in order to be added. So is such a feature critical enough to justify making the language even bigger?Writeup
In which case, C++ is doomed to die off as with that reasoning, they'd prefer to favour legacy stuff over introducing new features to make the language more elegant.Mendy
BTW, is there a strong reason for the auto keyword?Mendy
@Zach Saw - That's an amusing example to pick. In C++0x, the auto keyword has been repurposed and there is now an excellent reason to have it.Variolite
What defines how strong a reason is? Is it objective or subjective?Mendy
@Zach Saw - Consensus. It's subjective, but driven by the opinions of an entire group, not just one person. I think a strong case can be made for some sort of scoped using declaration, and possibly scoped aliases as well.Variolite
I guess what I was trying to say is it could always be argued that one should never need anything higher level than ASM. The committee members have always been overly conservative and the enhancements to the language have been very slow to come by. Most vendors have implemented some form of "property" keyword non-standard extension now, and the standard still hasn't caught up. IMHO, I don't think the committee have got the balance right.Mendy
It should also be noted that it's because this group is made up of people with different (business) interest that it's traditionally been extremely hard to get consensus on anything.Mendy
@Zach Saw - I believe there are function and class 'attributes' in C++0x. And I think that fits the definition of 'property' keyword.Variolite
@Omni: No they're totally different things. There is however a proposed solution for properties in C++0x but it called Implicitly callable functions (or ICFs) and Implicitly callable member functions (ICMFs). Not sure if it's been dropped from C++0x though.Mendy
@Zach: These features aren't "legacy staff". On the contrary, C++'s features were carefully selected to be general enough so they won't become "legacy" in the foreseen future.Lightproof
@ybungalobill: That's my point exactly. They're being overcautious. In making sure features won't become legacy in the foreseen future, the language itself will become legacy.Mendy
I'm having a bit of trouble understanding this whole using {} thing. That's a feature idea, not a currently-valid C++ construct, right? @ZachSaw, why did you accept this answer if it's not what you're looking for?Inlaid
@KyleStrand - It is a future idea, not a currently valid C++ construct, yes. If I might speak for Zack, I would say that he accepted it because it sparked the most interesting discussion in the comments and in combination with the comments explained how and why features get added to C++.Variolite
A
14

Sometimes I do this to achieve almost the same effect:

namespace detail {
    using namespace System;
    using namespace System::Network;
    using namespace System::Network::Win32::Sockets;

    class Foo : public Bar
    {
         void FooBar(Handle handle, Error& error);
    };
}
using detail::Foo;
Amharic answered 6/12, 2010 at 5:17 Comment(6)
Please read the question carefully before answering.Mendy
I saw that. But the question is a bit pointless cause it most likely has no answer. A lot of things in C++ standard don't make sense.Amharic
Sometimes the answer to "why isn't X allowed?" is that C++ provides another way of achieving the same thing -- as is demonstrated here.Sirloin
This is what I've been trying to work our if it's considered bad practice or not. People seem to say that don't use using namespace in a header - but I wonder if they mean in the global space. Is it ok to use using std:vector inside a header inside your own namespace before declaring a class that uses a lot of vectors?Jessiajessica
This has the side effect of the user of Foo needing to know it's in the detail namespace when Foo needs to be fully qualified to resolve ambiguities (::Foo). So no, what's demonstrated here is not achieving the same thing.Mendy
@Jessiajessica The issue with having using-directives inside a header is that any source file that includes that header, or any source file that includes any other file that includes that header, has those using-directives in it as well, very possibly without the writer of the source file even knowing about it. If it's limited in scope, such as by being placed inside a separate namespace, then this isn't as much of an issue.Flagelliform
V
12

Given that using declarations at class scope are not inherited, this could work. The name would only be valid inside that class declaration, or inside the declarations of nested classes. But I think it's sort of overloading the concept of a class with an idea that should be larger.

In Java and Python individual files are treated in a special way. You can have import declarations that inject names from other namespaces into the file. These names will (well, not exactly with Python, but it's too complicated to explain here) only be visible within that file.

To me that argues for this sort of ability not being tied to a class declaration, but given a scope of its own instead. This would allow injected names to be used in several class declarations if it made sense, or even in function definitions.

Here is an idea I prefer because it allows these things while still giving you the benefits of a class level using declaration:

using {
   // A 'using' block is a sort of way to fence names in.  The only names
   // that escape the confines of a using block are names that are not
   // aliases for other things, not even for things that don't have names
   // of their own.  These are things like the declarations for new
   // classes, enums, structs, global functions or global variables.
   // New, non-alias names will be treated as if they were declared in
   // the scope in which the 'using' block appeared.

   using namespace ::std;
   using ::mynamespace::mytype_t;
   namespace mn = ::mynamespace;
   using ::mynamespace::myfunc;

   class AClass {
     public:
      AClass(const string &st, mytype_t me) : st_(st), me_(me) {
         myfunc(&me_);
      }

     private:
      const string st_;
      mn::mytype_t me_;
   };
// The effects of all typedefs, using declarations, and namespace
// aliases that were introduced at the level of this block go away
// here.  typedefs and using declarations inside of nested classes
// or namespace declarations do not go away.
} // end using.

// Legal because AClass is treated as having been declared in this
// scope.
AClass a("Fred", ::mynamespace::mytype_t(5));

// Not legal, alias mn no longer exists.
AClass b("Fred", mn::mytype_t);

// Not legal, the unqualified name myfunc no longer exists.
AClass c("Fred", myfunc(::mynamespace::mytype_t(5));

This is analogous to declaring a block for local variables in a function. But in this case you are declaring a very limited scope in which you will be changing the name lookup rules.

Variolite answered 6/12, 2010 at 5:24 Comment(26)
Again, the question is, "Is there a strong reason against introducing scoped using-directive in structs and classes in the C++ language?"Mendy
@Zach Saw - I fixed my answer to actually answer your question. :-)Variolite
No it won't be the same as my idea. Derived classes won't inherit the using-directive - it's scoped within the class only. So no problem with the ambiguities as per your typedef.Mendy
@Zach Saw - Hmmm... OK, so that problem doesn't exist because you're right, other kinds of using declarations are not inherited. I still like my using scope idea better because I think it's more flexible and clearer.Variolite
@Omnifarious: Again, that doesn't answer the question. You're arguing that we should have the feature in the language -- the question is, why should we not have it.Mendy
@Zach Saw - My argument is that a different, related feature would actually be better and clearer.Variolite
@Omni: That's not a feature yet. And we're not discussing how it should look like - we're discussing why shouldn't we have it.Mendy
@Zach Saw - And I'm saying we shouldn't have it in the form you're talking about because it's too limited in scope (pun intended) that way.Variolite
@Omni: That's just an example - to help with the understanding of what I meant by "scoped using-directive within a struct/class declaration". Unless you can find a portable C++ language feature that fulfills this criteria in a more elegant manner, I can't accept your answer.Mendy
@Omni: Personally, I prefer it to be limited in scope. But that's not what this question is all about. I suppose it would make a great topic for another day if we've decided that there's no reason why C++ shouldn't support this feature to begin with.Mendy
@Zach Saw - Personally, I think that the existence of an idea for a more general and/or clearer feature is a perfectly good reason to be against the more limited and less clear feature.Variolite
@Omni: It's your idea of scope-restricted using-directive vs mine. Again, that's a topic for another day but right now, let's establish if there's a strong reason against introducing it to the C++ language.Mendy
Usually, the standards committee works the opposite way. Is there a strong reason for including it in the language? C++ is already a huge language. Every new feature really has to justify the cost in order to be added. So is such a feature critical enough to justify making the language even bigger?Writeup
In which case, C++ is doomed to die off as with that reasoning, they'd prefer to favour legacy stuff over introducing new features to make the language more elegant.Mendy
BTW, is there a strong reason for the auto keyword?Mendy
@Zach Saw - That's an amusing example to pick. In C++0x, the auto keyword has been repurposed and there is now an excellent reason to have it.Variolite
What defines how strong a reason is? Is it objective or subjective?Mendy
@Zach Saw - Consensus. It's subjective, but driven by the opinions of an entire group, not just one person. I think a strong case can be made for some sort of scoped using declaration, and possibly scoped aliases as well.Variolite
I guess what I was trying to say is it could always be argued that one should never need anything higher level than ASM. The committee members have always been overly conservative and the enhancements to the language have been very slow to come by. Most vendors have implemented some form of "property" keyword non-standard extension now, and the standard still hasn't caught up. IMHO, I don't think the committee have got the balance right.Mendy
It should also be noted that it's because this group is made up of people with different (business) interest that it's traditionally been extremely hard to get consensus on anything.Mendy
@Zach Saw - I believe there are function and class 'attributes' in C++0x. And I think that fits the definition of 'property' keyword.Variolite
@Omni: No they're totally different things. There is however a proposed solution for properties in C++0x but it called Implicitly callable functions (or ICFs) and Implicitly callable member functions (ICMFs). Not sure if it's been dropped from C++0x though.Mendy
@Zach: These features aren't "legacy staff". On the contrary, C++'s features were carefully selected to be general enough so they won't become "legacy" in the foreseen future.Lightproof
@ybungalobill: That's my point exactly. They're being overcautious. In making sure features won't become legacy in the foreseen future, the language itself will become legacy.Mendy
I'm having a bit of trouble understanding this whole using {} thing. That's a feature idea, not a currently-valid C++ construct, right? @ZachSaw, why did you accept this answer if it's not what you're looking for?Inlaid
@KyleStrand - It is a future idea, not a currently valid C++ construct, yes. If I might speak for Zack, I would say that he accepted it because it sparked the most interesting discussion in the comments and in combination with the comments explained how and why features get added to C++.Variolite
O
1

You can use a typedef inside the class declaration to achieve the same

class Foo : public Bar
{
      typedef System::Network::Win32::Sockets::Handle Handle;
      typedef System::Network::Win32::Sockets::Error Error;

      void FooBar(Handle handle, Error& error);
};
Oceania answered 4/3, 2014 at 9:9 Comment(5)
In which case you'll end up typedef'ing all the types under System::Network::Win32::Sockets namespace used in the class. Not only that, you'll have to then typedef all the types under other namespaces used in that class too! You do realise that's not practical don't you? And in the CPP file, you'll still have to fully qualify the types.Mendy
Your are of course correct. But thats not necessarily a bad thing. Usually one wont want (or shouldnt) to use a complete namespace anyways, but rather certain identifiers (eg Handle). And yes its a bit of a pain having to do the extra typing, just as it would be to write "using System::Network::Win32::Sockets::Handle" But that doesnt mean that one should prefer writing ""using System::Network::Win32::Sockets" just because its less typing. Its a trade off between fine grained control/clarity vs more text.Oceania
Correction : prefer writing ""using System::Network::Win32::Sockets" ..And in the .cpp file its generally ok to use the whole namespace (and avoid repeating the typedefs) since it wont be included in other headers and dirty the global namespaceOceania
Don't assume everything is about the programmer being lazy. Typing more is the least of my concerns. Typedefing so many types in each class makes the code extremely verbose. Readability suffers.Mendy
Yes. And making everything in namespace "available" when you dont need it all is error prone and pollutes the current namespace. You only need to typedef the identifiers you need, which typically isnt that many. As I said its a compromise. More control with more text, or vice versa.Oceania
E
0

Maybe namespace alias?

namespace MyScope = System::Network::Win32::Sockets;
Erelia answered 6/12, 2010 at 3:12 Comment(6)
With so many namespaces to alias (plus the fact that you can't scope the alias), you'll soon end up with the same problem as globally using the namespace.Mendy
@Zach Saw - Have you tested to make sure they can't be restricted a scope?Variolite
MSVC2010 doesn't allow that. C++Builder2010 doesn't either.Mendy
@Zach Saw - g++ 4.5.1 allows it to be restricted to function scope. But it can't be restricted to class scope. Hmm... That's not so useful.Variolite
So no compiler allows it then. Who voted this answer up???Mendy
I think you have to remap nested namespaces to a flat one, two or three deep oneErelia
M
-2

The obvious benefit of namespaces is that they allow you to avoid naming conflicts. However, by introducing an entire namespace into a class declaration, this benefit is voided. It's entirely possible that a function in the System namespace could conflict with your own Bar::MemberFunc function. You even note this in your sample code when you added the comment "no conflict with this".

You obviously don't want to introduce entire namespaces into your class like this:

using namespace System;
using namespace System::Network;
using namespace System::Network::Win32::Sockets;

And you can't add more narrowly scoped using statements like this into a class declaration. Putting these directly into a class declaration is illegal.

using System::Network::Win32::Sockets::Handle;
using System::Network::Win32::Sockets::Error;

What you can do is use the unnamed namespace. So, your header file would look something like this:

namespace {
    using System::Network::Win32::Sockets::Handle;
    using System::Network::Win32::Sockets::Error;
}

class Foo : public Bar
{
    using Bar::MemberFunc;

    // clean!
    void FooBar(Handle handle, Error& error /*, more declarations*/);
};

This has three distinct advantages.

  1. The contents of entire namespaces are not introduced. This helps to more easily avoid naming conflicts.
  2. You have a list, before your class declaration, of what your class depends upon to work correctly.
  3. Your function declarations are clean.

Please correct me if I'm wrong; I only briefly tested this. Quickly wrote up an example, and it compiled.

Micelle answered 6/12, 2010 at 5:36 Comment(2)
The anonymous namespace is local to a translation unit, not to a file. That means the names in the anonymous namespace are available to all the other files that are part of that translation units, even if they're other header files. That can silently change the meaning of random header files.Variolite
Ambiguities could be solved as per usual, or via namespace alias. So your whole argument is moot. Also, your unnamed namespace is not scope restricted too - that's extremely bad.Mendy

© 2022 - 2024 — McMap. All rights reserved.