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?
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...
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});
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.
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.