do_decimal_point and do_thousands_sep Not Working

落花浮王杯 提交于 2019-12-04 02:31:02

问题


do_decimal_point and do_thousands_sep seem to be completely ignored by my stream.

What I want is to do is use a period for my thousands_sep and a comma for my decimal_point in get_money. So I override moneypunct but it is just ignored :(

struct punct_facet : public moneypunct<char> {
    char_type do_decimal_point() const { return ','; }
    char_type do_thousands_sep() const { return '.'; }
};

int main()
{
    istringstream USCurrency("1,234.56 -1,234.56 1.234,56 -1.234,56");
    USCurrency.imbue(locale(locale("en-US"), new punct_facet));
    int index = 0;
    long double value;

    do{
        value = 0.0;
        USCurrency >> get_money(value, true);
        cout << ++index << ": " << value << endl;
    } while (value == 123456.0 || value == -123456.0);
    return 0;
}

I would expect this to just output:

1: 123

But instead I get:

1: 123456
2: -123456
3: 123

What am I doing wrong? I'm using Visual Studio 2013, in case that may be obvious from the "en-US".

EDIT:

I've discovered when I place a break-point in do_decimal_point or do_thousands_sep that it is never hit. I'm not sure why not, but that information seems to be relevant to the problem.


回答1:


This solution is really just an explanation of the answer given here.

Both the copy constructor and assignment opperator are deleted by the moneypunct implementation. Which leaves two bad options for constructing punct_facet:

  1. Duplicate all moneypunct members in punct_facet and call all moneypunct virtual functions in the punct_facet constructor to initialize them. This has the obvious drawback of a punct_facet object being twice as fat as it should be and it's constructor running longer than is strictly necessary.
  2. Use pointers and a compiler specific knowledge of object layout to effect a copy construction from a moneypunct to a punct_facet. This has the obvious drawback of not being cross-platform and intentionally disregarding the standard implementation's design.

For this answer I have elected bad option 2, because the implementation of moneypunct is already compiler specific for any construction argument other than: "", "C", or "POSIX" and because there is an open bug against the deleted moneypunct copy constructor and assignment operator. (Incidentally if the moneypunct construction argument is adjusted option 2 works in gcc 5.1.0 as well, but it will not work in Clang 3.6.0.) Hopefully Microsoft will provide a more functional workaround for that bug soon and we won't have to use either bad option.

So if punct_facet is implemented like this:

template <typename T>
class punct_facet : public T {
private:
    void Init(const T* money){
        const auto vTablePtrSize = sizeof(void*);

        memcpy(reinterpret_cast<char*>(this) + vTablePtrSize, reinterpret_cast<const char*>(money) + vTablePtrSize, sizeof(T) - vTablePtrSize);
    }
protected:
    typename T::char_type do_decimal_point() const {
        return typename T::char_type(',');
    }

    typename T::char_type do_thousands_sep() const {
        return typename T::char_type('.');
    }
public:
    punct_facet(){
        Init(&use_facet<T>(cout.getloc()));
    }

    punct_facet(const T* money){
        Init(money);
    }
};

You can construct with either of the punct_facet constructors and you will get your expected output:

123

To use the default constructor you'd need to add cout.imdue(locale("en-US")); at the top of main and change your imdue statement to:

USCurrency.imbue(locale(locale("en-US"), new punct_facet<moneypunct<char, true>>()));

To use the custom constructor you'd only need to change your imdue statement to:

USCurrency.imbue(locale(locale("en-US"), new punct_facet<moneypunct<char, true>>(&use_facet<moneypunct<char, true>>(locale("en-US")))));

The default constructor is preferable as a discrepancy between your template type and your constructor argument could result in some bad behavior.

One minor note, your USCurrency is not using international currency format, so there's no need to use moneypunct<char, true>, moneypunct<char> will work just fine. Just remember to change it everywhere as a discrepancy between the template arguments to punct_facet and the arguments used in get_money will again result in the unexpected behavior you were seeing.



来源:https://stackoverflow.com/questions/31245857/do-decimal-point-and-do-thousands-sep-not-working

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!