Max Nov
Max Nov

Reputation: 23

Can't understand how to make istream manipulator

I want to make an int32_le_read(x) manipulator that reads binary from a file and writes to x. I implemented the int32_bin_manip class

class bin_manip
{
    public:
     bin_manip();
     ~bin_manip();
     friend std::istream& operator>>(std::istream& is,bin_manip& obj);
     friend std::ostream& operator<<(std::ostream& os, const bin_manip& obj);
     virtual std::ostream& write(std::ostream& os) const = 0;
     virtual std::istream& read(std::istream& is) = 0;
};

class int32_bin_manip : public bin_manip
{
    public:
        int32_t* x;
        int32_bin_manip(int& x);
        std::ostream& write(std::ostream& os) const;
        std::istream& read(std::istream& is);
};

for which operator>> and operator<< exist. How do I make a manipulator for istream of it now? For the ostream manipulator, I just created a function that returns an instance of this class and everything worked fine.

int32_bin_manip write_le_int32(int& x)
{
    return int32_bin_manip(x);
}

But I can't do that with the istream manipulator, because its argument is int32_bin_manip& obj, unlike const int32_bin_manip& obj, which means I can't create a function that returns an instance of int32_bin_manip and use it like istream >> int32_le_read(x), because it will bind the temporary instance to a non-const reference which is forbidden. So how do I need to implement such a manipulator?

Upvotes: 2

Views: 75

Answers (2)

fabian
fabian

Reputation: 82531

You could simply add another overload taking an rvalue reference to int32_bin_manip; rvalue references work as lvalue references to non-const inside the function body of a function with the rvalue reference as argument.

Note: write functionality removed for brevity in the following example, since there's no issue with using const in this case.

class bin_manip
{
public:
    virtual std::istream& read(std::istream& is) = 0;

    friend std::istream& operator>>(std::istream& is, bin_manip& obj)
    {
        obj.read(is);
        return is;
    }
};

class int32_bin_manip : public bin_manip
{
    int32_t& m_x;
public:
    constexpr int32_bin_manip(int32_t& x)
        : m_x(x)
    {
    }

    std::istream& read(std::istream& is) override
    {
        is >> m_x;
        return is;
    }

    friend std::istream& operator>>(std::istream& is, int32_bin_manip&& obj)
    {
        obj.read(is);
        return is;
    }
};

int main() {
    int32_t i;
    std::cin >> int32_bin_manip(i);
    std::cout << i << '\n';
}

Depending on your design you may want to introduce a function with 2 seperate overloads for read function calls on lvalues and rvalue references, if this suits your design better:

class bin_manip
{
public:
    virtual std::istream& read(std::istream& is) & = 0; // this one works just for lvalues, not for rvalue references

    friend std::istream& operator>>(std::istream& is, bin_manip& obj)
    {
        obj.read(is);
        return is;
    }
};

class rvalue_enabled_bin_manip : public bin_manip
{
public:

    using bin_manip::read;

    virtual std::istream& read(std::istream& is)&& // overload for rvalues
    {
        // fall back to standard, non-rvalue
        return read(is);
    }

    friend std::istream& operator>>(std::istream& is, rvalue_enabled_bin_manip&& obj)
    {
        std::move(obj).read(is);
        return is;
    }
};

class int32_bin_manip : public rvalue_enabled_bin_manip
{
    int32_t& m_x;
public:
    constexpr int32_bin_manip(int32_t& x)
        : m_x(x)
    {
    }

    std::istream& read(std::istream& is) & override
    {
        is >> m_x;
        return is;
    }
};

Upvotes: 0

Ted Lyngmo
Ted Lyngmo

Reputation: 118097

You can't bind a non-const lvalue reference to a temporary, but you don't have to since you a not modifying the object itself but instead another object that it's pointing at, so make the operator>> take it by const& too:

class bin_manip {
public:
    virtual ~bin_manip() = default; // <- added
    friend std::istream& operator>>(std::istream& is, const bin_manip& obj);
    //                                                ^^^^^ added
    friend std::ostream& operator<<(std::ostream& os, const bin_manip& obj);

    virtual std::ostream& write(std::ostream& os) const = 0;
    virtual std::istream& read(std::istream& is) const = 0;
    //                                           ^^^^^ added
};

Demo

Upvotes: 3

Related Questions