10

It has been a long time since my last use of c++, coming back from java and python, I have a question about good practices on c++:

I wanted to keep a semantic code about some really simple objects, let's say we have objects Tag and File, which both only are std::string and a class TagManager which contains several functions using Tags and Files.

My question is, is it better to create a custom type representing these trivial objects or to use them directly for what they are?

To be more specific I have could have one of those definitions for a function:

TagIterable myFunction(Tag tag, File file);
std::vector<Tag> myFunction(Tag tag, File file);
std::vector<std::string> myFunction(std::string tag, std::string file);

I prefer the first solution because I like to keep a semantic code, on the other hand doing it requires the user to check what these types are, so it reduce the simplicity of use.

I also read on another question (Thou shalt not inherit from std::vector) 'Do not produce new entities just to make something to look better.'

So what do you do in such situation; what does the C++ philosophy recommend to do?

Community
  • 1
  • 1
aveuiller
  • 1,511
  • 1
  • 10
  • 25
  • 1
    File stands for an actual file? or does it mean it's a filename? Then it'd be a better idea to call it filename as it could mislead other people using your code... – xmoex Nov 24 '13 at 12:16

5 Answers5

5

I usually use typedefs in such cases:

  • Even though typedefs don't introduce new types or nasty derivatives of std::vector<> themselves, the aliases may still be possibly annoying indirections for those using your code.
  • On the other hand, they could be exposed and documented well (provide an example), so that everybody uses them from start.
  • They help to avoid build breakers, in case the type needs to be changed (provided, that special std::string features aren't used or are provided in the replacement, in your specific case).
  • So, if required, a typedef could later be changed to alias a new class with extra properties and functions, or just for type safety.
  • They allow easy tracking of usage locations, as well as refactoring in most IDEs.
namespace mylib
{
    typedef std::string Tag; //perhaps 'TagType' or 'tag_type', to follow the naming
                             //conventions of the standard library, e.g.
                             //std::vector::value_type
    typedef std::string File;//Same as above, 'FileName' might be more appropriate

    typedef std::vector<Tag> TagIterable;
                             //Could be named 'TagContainer',
                             //`TagVector` or `TagSet` when using std::set,
                             //to match the terminology of C++

    TagIterable myFunction(const Tag& tag,const File& file);
                             //One may use references to const here,
                             //to explicitly avoid unnecessary copies

};
Sam
  • 7,778
  • 1
  • 23
  • 49
2

Its called encapsulation (also known as data hiding). If encapuslating the concept of a File and a Tag adds value, then go ahead and do it, otherwise you're just adding complexity. It could be that later you decide to use std::wstring instead of a simple std::string internally in the classes, which you could easily change if you create the Tag and File classes, and the users of the classes most likely wont be impacted.

As for inheriting from std::vector, that is indeed something you should avoid. But remember: inheriting and encapsulation are 2 completely different concepts.

Brady
  • 10,207
  • 2
  • 20
  • 59
2

Being the type freak that I am, I always include new types.

Writing the wrapper code is easy enough:

class Tag {
public:
    Tag() {}
    explicit Tag(std::string const& v): _value(v) {}
    std::string const& get() const { return _value; }
private:
    std::string _value;
}; // class Tag

And that's it!

typedef are nice and all, but it's too easy to mix them up and use operations that do not make sense: what does Tag::substr means ?

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
1

i'd preferr to use what c++ provides you with unless it's really necessary to make an alias. Why don't just use self-speaking identifyers? My Solution would look like:

std::vector<std::string> getTagsFromFile(std::string tag, std::string fileName);
xmoex
  • 2,602
  • 22
  • 36
  • In general simpler is always better. Something else to consider is that encapsulating it will make it future-proof to implementation changes. – Brady Nov 24 '13 at 13:18
0

There are some valid use cases for wrapping (simple) types:

  • Makeing types distinct. For example an identifier which will be unique for some context.
  • Give a type a unit (US gallon 3.79 vs. imperial gallon 4.546) - for example boost::unit. The unit is actually an additional information.
  • ...

For each wrapping you would create a distinct class (template) to ensure type safety. A simple typedef would not encapsulate anything, but clarify usage, only.