mercredi 25 mai 2016

Pattern for custom operator >> istream

It is often required for some non-scalar structure X to provide input operator std::istream & operator >> (std::istream &, X &);. I tried to implement it in a 3 slightly different ways:

#include <istream>
#include <ostream>
#include <tuple>

#include <cstddef>

struct rectangle
{

    std::size_t l, t, r, b; // left, top, right, bottom

    friend
    std::istream &
    operator >> (std::istream & in, rectangle & R)
    {
#if 0
        // v1
        do {
            if (!(in >> R.l)) {
                break;
            }
            if (!(0 < R.l)) {
                break;
            }
            if (!(in >> R.t)) {
                break;
            }
            if (!(0 < R.t)) {
                break;
            }
            if (!(in >> R.r)) {
                break;
            }
            if (!(0 < R.r)) {
                break;
            }
            if (!(in >> R.b)) {
                break;
            }
            if (!(0 < R.b)) {
                break;
            }
            return in;
        } while (false);
        in.setstate(std::ios::failbit);
        return in;
#elif 0
        // v2
        if (in >> R.l) {
            if (0 < R.l) {
                if (in >> R.t) {
                    if (0 < R.t) {
                        if (in >> R.r) {
                            if (0 < R.r) {
                                if (in >> R.b) {
                                    if (0 < R.b) {
                                        return in;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        in.setstate(std::ios::failbit);
        return in;
#elif 0
        // v3
        if (!(in >> R.l)) {
            return in;
        }
        if (!(0 < R.l)) {
            in.setstate(std::ios::failbit);
            return in;
        }
        if (!(in >> R.t)) {
            return in;
        }
        if (!(0 < R.t)) {
            in.setstate(std::ios::failbit);
            return in;
        }
        if (!(in >> R.r)) {
            return in;
        }
        if (!(0 < R.r)) {
            in.setstate(std::ios::failbit);
            return in;
        }
        if (!(in >> R.b)) {
            return in;
        }
        if (!(0 < R.b)) {
            in.setstate(std::ios::failbit);
            return in;
        }
        return in;
#else
#error do select body!
#endif
    }

    friend
    std::ostream &
    operator << (std::ostream & out, rectangle const & R)
    {
        return out << R.l << ' ' << R.t << ' ' << R.r << ' ' << R.b;
    }

    bool
    operator == (rectangle const & R) const
    {
        return std::tie(l, t, r, b) == std::tie(R.l, R.t, R.r, R.b);
    }

};

// main.cpp
#include <iostream>
#include <sstream>

#include <cstdlib>
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <cassert>

int
main()
{
    std::stringstream ss;
    rectangle lhs{1, 2, 3, 4};
    ss << lhs;
    rectangle rhs;
    ss >> rhs;
    assert(lhs == rhs);
    return EXIT_SUCCESS;
}

Live example.

Every way has its own advantages and disadvantages:

  • v1:
    • pros:
      • plain code
      • minimal duplication of code (1 setstate())
      • just two return statements
    • cons:
      • (semantically) goto-like do {} while (false); construction (is it really a bad practice?)
  • v2:
    • pros:
      • minimal duplication of code (1 setstate())
      • just two return statements
    • cons:
      • too deep nesting of if statements
  • v3 (naive):
    • pros:
      • plain code
    • cons:
      • duplication of code (setstate() euqal to number of constituents)
      • plenty of return statements

I like v1 way, but are there better patterns to implement operator >>?

Aucun commentaire:

Enregistrer un commentaire