std::codecvt::do_in method overloading vs the rest

2019-08-18 03:24发布

问题:

I have overloaded do_in method of std::codecvt:

#include <iostream>
#include <locale>
#include <string>

class codecvt_to_upper : public std::codecvt<char, char, std::mbstate_t> {
public:

    explicit codecvt_to_upper(size_t r = 0) : std::codecvt<char, char, 
                                                          std::mbstate_t>(r) {}
protected:
    result do_in(state_type& state, const extern_type* from,
                 const extern_type* from_end, const extern_type*& from_next,
                 intern_type* to, intern_type* to_end, intern_type*& to_next)
                   const;

    result
    do_out(state_type& __state, const intern_type* __from,
            const intern_type* __from_end, const intern_type*& __from_next,
            extern_type* __to, extern_type* __to_end,
            extern_type*& __to_next) const {
        return codecvt_to_upper::ok;
    }

    result
    do_unshift(state_type& __state, extern_type* __to,
            extern_type* __to_end, extern_type*& __to_next) const {
        return codecvt_to_upper::ok;
    }

    int
    do_encoding() const throw () {
        return 1;
    }

    bool
    do_always_noconv() const throw () {
        return false;
    }

    int
    do_length(state_type&, const extern_type* __from,
            const extern_type* __end, size_t __max) const {
        return 1;
    }

    int
    do_max_length() const throw () {
        return 10;
    }
};

codecvt_to_upper::result codecvt_to_upper::do_in(state_type& state, 
                  const extern_type* from, const extern_type* from_end, const 
                  extern_type*& from_next, intern_type* to, intern_type* 
                  to_end, intern_type*& to_next) const {
    codecvt_to_upper::result res = codecvt_to_upper::error;
    const std::ctype<char>& ct = std::use_facet<std::ctype<char> >( 
                                                               std::locale());

    const extern_type* p = from;
    while( p != from_end && to != to_end) {
        *to++ = ct.toupper( *p++);
    }
    from_next = p;
    to_next = to;
    res = codecvt_to_upper::ok;
    return res;
}

and used this way:

int main(int argc, char** argv) {

    std::locale ulocale( std::locale(), new codecvt_to_upper);

    std::cin.imbue( ulocale);

    char ch;
    while ( std::cin >> ch) {
        std::cout << ch;
    }
    return 0;
}

but do_in overload is not being called. Have I overloaded it correctly? Which method of std::codecvt<char, char, std::mbstate_t> (and how) do I have to change to make my facet calling do_in method?

回答1:

I think the first thing that should be addressed is that the std::codecvt family facets are only used by std::basic_filebuf because code conversion is only needed when dealing with an external device. In your code, you were imbuing the locale into std::cin, which has a buffer that does not do code conversion.

Of course, it is still possible to perform code conversion within the program, but the thing about your facet that was preventing your code from working was that it inherited from a specialization of std::codecvt<> that cannot do conversions. The char => char specialization of std::codecvt<> doesn't define a conversion, therefore do_in() won't be called because it is unnecessary to convert between the two types.

I tried running your code, but changing the inherited facet to std::codecvt<wchar_t, char, std::mbstate> and used wide-character file streams and it worked.

If you want this to work for narrow-character streams as well I would suggest creating a stream buffer that forwards uppercase characters through underflow().