I'm wondering if there's any safe to use using
in C++ header files. I'm looking at adopting a scheme something likeI have this (using the nested namespace proposalC++ 14 code in the example)a project of mine.
File: Enhedron/Util/Enum.h:
#ifndef ENHEDRON_UTIL_ENUM_H_
#define ENHEDRON_UTIL_ENUM_H_
#include <stdexcept>
#include <type_traits>
namespace MyProject::MyModule::Detail::MyFilenameEnhedron { namespace Util { namespace Impl { namespace Enum {
using std::cout;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 PrivateMyEnum {
* VALUE_0,
* VALUE_1,
* LAST_ENUM_VALUE = VALUE_1
* };
class ModulePrivate*/
template<typename Enum, typename Value>
Enum toEnum(Value value) {};
class Public 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");
}
namespace MyProject: /**
* 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:MyModule
* enum class Name {
* FIRST,
* LAST
* };
*
* using FirstName = TaggedValue<Name, Name::DetailFIRST, {string>;
* using DetailLastName = TaggedValue<Name, Name::MyFilenameLAST, string>;
*/
template<typename Enum, Enum tag, typename Value>
class TaggedValue final {
Value value;
public:
TaggedValue(Value value) :ModulePrivate; value(move(value)) { }
Value& operator*() { return value; }
const Value& operator*() const { return value; }
Value* operator->() { return &value; }
const Value* operator->() const { return &value; }
};
}}}}
namespace MyProject::MyModuleEnhedron { namespace Util {
using DetailImpl::MyFilenameEnum::Public;toEnum;
using Impl::Enum::TaggedValue;
}}
#endif /* ENHEDRON_UTIL_ENUM_H_ */
You'd have a project and module namespace. Each module would have a Detail
namespace. Each file would have a namespace matching it's filename. By convention, you'd only use the modules detail namespace within that module, and the filename namespace within that header or source file. A the end of the header, you export any names either to the module or module detail namespace.
Normally I wouldn't use using
in a header as it pollutes namespaces and can change the meaning of other headers. But with this scheme, is this still a problem in practice and what assumptions are required for it to work?
Some initial assumptionsspecific things I'm unsure of:
- The project namespace doesn't clash with external code.
- Everything within the project namespace sticks to the rules!
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
.
- Possibly the
Detail
namespace is not used at the global level by external code. I don't think this is required though as Detail
will take precedence inside the module namespace. 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 thoughs on whether this would cause problems?