Search code examples
c++namespacescoding-stylenaming

Nested namespace for cleaner code


I am currently writing an adapter to convert types between two frameworks A <-> B. I have a little bit of trouble with the names, as they are getting quite long and unreadable in my opinion. I then decided to use namespaces in a quite nested way, I have seen rarely anywhere else.

OLD:

TypeA mylib::cvtToAtype(TypeB) {}
TypeB mylib::cvtToBtype(TypeA) {}

NEW:

TypeA mylib::cvt::to_a::type(TypeB) {}
TypeB mylib::cvt::to_b::type(TypeA) {}

Would you consider this a good style or do you see heavy downsides? I think it looks quite clean, however it can be really misused if someone decides to "use namespaces". Then type is not really a unique name to identify what the function is doing. Additionally a namespace called "cvt" might also not be super unique.

What do you think?


Solution

  • So, you started with:

    TypeA mylib::cvtToAtype(TypeB) {}
    TypeB mylib::cvtToBtype(TypeA) {}
    

    Your stated problem: function names "getting quite long and unreadable". You propose:

    mylib::cvt::to_a::type
    

    Firstly, "cvt" is not going to be painlessly recognised as "convert" by readers/maintainers of your code. The trailing ::type seems pointless to me. Anything called "to_" can be expected to be a conversion, so I don't see the point in the ::cvt:: level.

    For whatever it's worth, some alternatives...

    Constructors

    You could consider creating constructors [explicit] TypeA::TypeA(TypeB) and [explicit] TypeB::TypeB(TypeA) so the client usage is simply:

    TypeA a{b};
    functionThatWantsB(TypeB{a});
    

    Cast/conversion template

    If you want to keep the functions separate from the TypeA/B classes, a "cast" style template with specialisations is a nice option:

    template <typename TypeTo, typename TypeFrom>
    TypeTo convert(const TypeFrom&);
    
    template <>
    TypeB convert<TypeB, TypeA>(const TypeA& a) { return TypeB{...}; }
    
    template <>
    TypeA convert<TypeA, TypeB>(const TypeB& a) { return TypeA{...}; }
    

    Client usage is then the familiar casting notation: e.g. convert<TypeB>(a). This is considerably more powerful than cvtToAtype because if the destination type is abstract - a parameter in a template, an alias from using or typedef - you can still cast.

    Knowing the destination type because it's a function argument

    Another option is to have a function argument of the convert-to type, so the caller doesn't have to type it out as part of the conversion function name or a template parameter:

    TypeA& loadFrom(TypeA&, const TypeB&) { ... }
    TypeB& loadFrom(TypeB&, const TypeA&) { ... }
    

    Such use of the compile-time polymorphism of overloads means the conversion code doesn't need to be updated every time the variables involved change between supported types.