3
\$\begingroup\$

I have this C++ 14 code in a project of mine.

Enhedron/Util/Enum.h:

#ifndef ENHEDRON_UTIL_ENUM_H_
#define ENHEDRON_UTIL_ENUM_H_

#include <stdexcept>
#include <type_traits>

namespace Enhedron { namespace Util { namespace Impl { namespace Enum {
    using std::runtime_error;
    using std::underlying_type_t;

    /**
     * Convert a value to an enum safely. The enum must have "LAST_ENUM_VALUE" as the last
     * enumerated value, and must be a simple enum starting at 0 and being densely populated. For example:
     *
     *      enum class MyEnum {
     *          VALUE_0,
     *          VALUE_1,
     *          LAST_ENUM_VALUE = VALUE_1
     *      };
     */
    template<typename Enum, typename Value>
    Enum toEnum(Value value) {
        if (value >= 0 && value <= static_cast<underlying_type_t<Enum>>(Enum::LAST_ENUM_VALUE)) {
            return static_cast<Enum>(value);
        }

        throw runtime_error("Value out of range for enum");
    }

    /**
     * Tag a type with an enumerated value to create a distinct type. Useful to differentiate values of the same type.
     * For example, to stop first and last names being interchangeable in the type system:
     *      enum class Name {
     *          FIRST,
     *          LAST
     *      };
     *
     *      using FirstName = TaggedValue<Name, Name::FIRST, string>;
     *      using LastName = TaggedValue<Name, Name::LAST, string>;
     */
    template<typename Enum, Enum tag, typename Value>
    class TaggedValue final {
        Value value;
    public:
        TaggedValue(Value value) : value(move(value)) { }

        Value& operator*() { return value; }
        const Value& operator*() const { return value; }

        Value* operator->() { return &value; }
        const Value* operator->() const { return &value; }
    };
}}}}

namespace Enhedron { namespace Util {
    using Impl::Enum::toEnum;
    using Impl::Enum::TaggedValue;
}}

#endif /* ENHEDRON_UTIL_ENUM_H_ */

Some specific things I'm unsure of:

  • Is there a better way to define TaggedValue so that you don't have to mention the enum type and the enum value you want to use? i.e. getting the template argument Enum inferred from the type of tag.

  • I'm using a namespace scheme where every header file has a namespace Enhedron::Module::Impl::FileName which is by convention private to that file. Then at the end of the file, it will export those names into the module namespace Enhedron::Module. My concern is that use of using inside the "private" namespace. Any thoughts on whether this would cause problems?

\$\endgroup\$
4
\$\begingroup\$

1. Auto Non-type template parameter

No, not possible, sorry, see this on SO. I wish it could be done... but no, not in current standard.

2. Complex namespaces and using

It is not causing problems to users of the header, but the question is: who will maintain the code? You? Only you?

If the answer is yes, then it sounds too complex for me. It would be much easier to have one header (e.g. common.hpp) with the usings within one private namespace (e.g. firda::detail in my case) where any implementation will go inside this namespace (e.g firda::detail::something) automatically sharing all the usings specified in one file like a convention (common usage, something anybody needs to read before altering your code).

To be honest, I use such header importing selected features to my global namespace (firda). It is not adviced to use using namespace std and the same apply to my namespace (do not use using namespace firda, I am the only one allowed to do so in my own projects).

You can run into problems after adding usings to that common header, but it is your code and you should be able to manage it.

If the answer is no, than you need some common convention and from my perspective, it is much easier to simply forget about usings (except inside functions/methods). You can use shortcuts (namespace alias) if you really need to (e.g. namespace ns1 = some::complex::namespace::chain) placed usually on top of the header (to be easily found). Writing std::something or ns1::something is not so much typing and explicity is much better for multiple-coworker scenairo.

If you have the convention to place all such usings on top of the header, than it can be found by anybody sharing this convention. So, the idea is not that bad after all, but I would still prefer namespace aliases and std:: prefixes.

\$\endgroup\$
  • \$\begingroup\$ Yes. Rare is the namespace guaranteed to only contain specific listed symbols, standard guarantees are only what it will definitely contain as a minimum. And if you have no idea what you put in your own namespace, you are dead meat anyway. \$\endgroup\$ – Deduplicator Nov 7 '15 at 19:43
3
\$\begingroup\$

I don't see the point in trying to use using (even in this protected situation).

// 26 letters
using std::runtime_error;

// later (saved 5 characters (so you need to use it 6 times to save typing)).
throw runtime_error("Message");

// Also it makes it harder for the reader. I am going to ask where is
// the definition of runtime_error (and then search to find your definition).
//
// Since I don't search manually I use tools (that jump to the defintion)
// vim with ctags.
//
// The tools will jump to the standard version which is correct;
// but in my mind I am thinking but hey
// this is supposed to be inside a namespace this can't be the correct
// definition, damm tools took me to the wrong place and now I have to start
// a manual search to verify that the tools got it correct.
//
// So though you think this is making things easier its not.
// It may make things slightly easier for you (since you are writing the code)
// but it does not make it easier for anybody reading the code. It just makes
// the cognitive load heavier and when learning a new code base there
// is enough issues with old crappy code that I don't need other
// eccentricities throw in on top of that.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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