4

I'm doing my first real project in C++, which is a simple CSV parser (in the very early stages right now), and I have the following in a header file:

class CsvReader {
public:
  // Actions to commit on each iteration of the CSV parser
  enum Action { ADD_CHAR, ADD_FIELD, NONE };

  // The possible states for each cell of a CSV
  enum State { START, IN_FIELD, IN_QUOTED_FIELD, IN_QUOTED_QUOTE };

  // Create the reader from a file
  explicit CsvReader(File& f);

  // Get a row from the CSV
  std::vector<std::string> get_row();
private:
  State m_state;
  LineReader m_lr;
  std::tuple<State, Action, bool> next(State s, const char& c);
};

When I want to implement the next function, I found it really annoying to constantly have to type out CsvReader:: before the enums because it made the code so verbose. So instead of having something like this in the implementation

std::tuple<CsvReader::State, CsvReader::Action, bool> next(CsvReader::State s, const char& c) {
  // more usage of CsvReader::
}

I did

typedef CsvReader::State State;
typedef CsvReader::Action Action;
std::tuple<State, Action, bool> CsvReader::next(State s, const char& c) {
  // function signature is much shorter and don't need to use CsvReader::
}

Except for the fact that I can't call anything else State or Action in the file are there any consequences to doing this? Are there any better solutions?

m0meni
  • 16,006
  • 16
  • 82
  • 141
  • 3
    alternate syntax: `using State = CsvReader::State;` – jaggedSpire Dec 01 '16 at 22:48
  • @jaggedSpire oh I didn't know that existed. Is there any difference? – m0meni Dec 01 '16 at 22:48
  • 1
    [Nope](http://stackoverflow.com/questions/10747810/what-is-the-difference-between-typedef-and-using-in-c11), unless you count that the using statement may be templated. – jaggedSpire Dec 01 '16 at 22:49
  • Can you use C++14 too? Or is C++11 a requirement? – skypjack Dec 01 '16 at 22:52
  • 1
    Alternatively, you may put everything related to parsing CSV into separate namespace, with all names moved to the top level of it. Client will use csv::CsvParser(...) and your implementation may use "using namespace csv;". – gudok Dec 01 '16 at 22:53
  • @skypjack c++14 is fine. I haven't actually learned much about it, which is why I left out the tag. I'll add it in. – m0meni Dec 01 '16 at 22:53
  • 1
    It may be worthwhile to mention there are a few FOSS CSV parsers out there, in C (e.g. as part of PostgreSQL, MonetDB, MySQL) and in C++. For the latter, see [this SO question](http://stackoverflow.com/questions/1120140/how-can-i-read-and-parse-csv-files-in-c) and [this Reddit thread](https://www.reddit.com/r/cpp/comments/3lrsbt/whats_your_go_to_modern_c_csv_reader/) and the links within. Note also that, if you need it for data whose generation you don't control, you must anticipate some off-spec input, which makes you have to be creative (something state machines are not so great at). – einpoklum Dec 01 '16 at 23:09
  • @einpoklum Thanks for the links, but I've actually already visited both of those, and even got a [CSV parser example added to PEGTL](https://github.com/ColinH/PEGTL/issues/33). I'd prefer to handroll the parser though just to learn more about FSMs and C++. – m0meni Dec 01 '16 at 23:11
  • @AR7: Sure, but remember other people read this page too... – einpoklum Dec 01 '16 at 23:18
  • @einpoklum ah right. My bad. – m0meni Dec 01 '16 at 23:24
  • Use `typedef` only to **add** information and abstraction to the codebase. Good: `typedef vector names;`, the new type `names` brings new information and abstraction. Bad: `typedef vector stringvec;`, you've done nothing except add noise. Worse: `typedef vector stringlist;`, welcome to this back alley where I will skin you alive. – screwnut Dec 03 '16 at 17:39

3 Answers3

6

Instead of this:

typedef CsvReader::State State;
typedef CsvReader::Action Action;
std::tuple<State, Action, bool> CsvReader::next(State s, const char& c) {
  // function signature is much shorter and don't need to use CsvReader::
}

you can do this:

auto CsvReader::next(State s, const char& c)
    -> std::tuple<State, Action, bool>
{
  // function signature is much shorter and don't need to use CsvReader::
}

For a separately compiled implementation file there isn't any particular problem with the first approach, but it's awkward and verbose.


By the way, const char& c is not meaningful. And in general, passing by reference to const is not meaningful for types smaller than a pointer, since it causes an address to be passed down at the machine code level. Just use const char c.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • So `auto` removes the need for using `CsvReader::` anywhere in the entire function? Is there any reason to not mark all functions `auto` then? – m0meni Dec 01 '16 at 23:02
  • 1
    @AR7: One can adopt `auto` as a single uniform syntax, and I do that. But what you can do depends on for example project guidelines. – Cheers and hth. - Alf Dec 01 '16 at 23:05
  • Alright well this was very helpful. I had absolutely no idea this syntax existed, and it's awesome – m0meni Dec 01 '16 at 23:08
2

Except for the fact that I can't call anything else State or Action in the file are there any consequences to doing this?

I have done this at my work in numerous files without any problem. I say go for it.

Are there any better solutions?

If you are able to use C++11, you can use using instead of typedef:

using State = CsvReader::State;

It is a more modern idiom. However, the net effect is the same for your use case.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
1

Are there any better solutions?

I wouldn't define it a better solution.
Anyway, as mentioned in the comments to the question, if you can use C++14 you can use also the auto return type.

Therefore this:

std::tuple<CsvReader::State, CsvReader::Action, bool>
CsvReader::next(CsvReader::State s, const char& c) {
    // do whatever you want
    return std::tuple<State, Action, bool>{};
}

Becomes this:

auto CsvReader::next(State s, const char& c) {
    // do whatever you want
    return std::tuple<State, Action, bool>{};
}

Note also that within the body of the function you don't have to explicitly qualify State or Action.

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • 1
    In general, omitting the return type places a burden on the reader to figure it out, so I prefer to state it explicitly. That also communicates the *intention*. With return type omitted it can be wrong (in particular, for beginners, this applies to references). – Cheers and hth. - Alf Dec 01 '16 at 23:04
  • @Cheersandhth.-Alf I usually omit return type on (short and easy to read) private member functions and explicitly specify them on public interface, where the intention is much more important. I cannot say if that's the case for the OP, but still this is a viable solution that's worth to be mentioned being it part of the language. – skypjack Dec 01 '16 at 23:07
  • Hm, I started updating my answer, adding this possibility, but then I realized **I don't know the rules** for exactly when you're permitted to omit the return type? – Cheers and hth. - Alf Dec 01 '16 at 23:09
  • @Cheersandhth.-Alf I think I didn't get your question. I used _omit_ the way you used it, I guess it's clear what I meant. Anyway, as a rule of thumb, I tend to use an `auto` return type on private, short and easy to read member functions. On the other side, when the return type is not easily deducible at a glance or for public member functions, I explicitly specify it as you mentioned in your question (well, I don't use a trailing return type usually, unless it's strictly required for some reasons). API must be _fully specified_ in my mind, I think we can reach an agreement on that at least. – skypjack Dec 01 '16 at 23:16
  • I mean, when the compiler can't deduce it, such as for `return {};`, then it certainly can't be omitted. And ditto for a converting return expression. But I don't know the general rules (probably because I don't use it much, hence the question) :( – Cheers and hth. - Alf Dec 01 '16 at 23:19
  • @Cheersandhth.-Alf Oh, ok, got it. It's half past midnight in Italy, but if this is an invite to look into the standard for that, I would be glad to help you integrating your answer tomorrow morning. :-) ... Made the deal? – skypjack Dec 01 '16 at 23:22
  • @Cheersandhth.-Alf Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/129599/discussion-between-skypjack-and-cheers-and-hth-alf). – skypjack Dec 02 '16 at 07:37