locale Facet Constructor Ignored

人盡茶涼 提交于 2019-12-12 07:27:13

问题


The locale Facet constructor:

Constructs a copy of other except for the facet of type Facet (typically deduced from the type of the argument) which is installed from the argument facet. If facet is NULL, the constructed locale is a full copy of other. The locale constructed in this manner has no name.

I try to construct using my Facet here, but when I put a break-point in my do_decimal_point and do_thousands_sep they are never called :(

I can see the Facet being passed in, but it's passed into standard library implementation files so I can't see if anything is ever done with it.

I've tried this on Visual Studio 2013, Clang 3.6.0, and gcc 4.9.2. All of them behave as though I had never passed in a Facet just using the other locale's behavior.

I can't find any bugs against this constructor in any of the compilers. I think I'm doing this all the right way. Why can't I get locale to construct using my Facet?

EDIT:

At the request of 0x499602D2 I have added an example. It's interesting to note that the Facet does seem to be picked up but not used with get_money. I'm linking a live example of this (which necessarily useslocale("C") instead of locale("en-US")):

class Foo : public std::moneypunct<char> {
protected:
    char_type do_decimal_point() const {
        cout << "Hit Foo::do_decimal_point";
        return ',';
    }
    char_type do_thousands_sep() const {
        cout << "Hit Foo::do_thousands_sep";
        return '.';
    }
};

int main()
{
    cout.imbue(locale(locale("en-US"), new Foo));

    const moneypunct<char>* temp = &use_facet<std::moneypunct<char>>(cout.getloc());

    cout << temp->decimal_point() << endl << temp->thousands_sep() << endl;

    istringstream USCurrency("1,234.56 -1,234.56 1.234,56 -1.234,56");
    USCurrency.imbue(cout.getloc());

    long double value;

    USCurrency >> get_money(value, true);

    return 0;
}

This outputs:

Hit Foo::do_thousands_sepHit Foo::do_decimal_point,
.

I would expect it to output:

Hit Foo::do_thousands_sepHit Foo::do_decimal_point,
.
Hit Foo::do_thousands_sepHit Foo::do_decimal_point

EDIT2:

It appears that moneypunct<char> can't be inherited from as it doesn't get constructed properly, unless it is constructed internally by the locale. Which at least on Visual Studio is a problem because it determines whether to use thousands_sep by the grouping. The work around may be to completely reimplement moneypunct<char>'s functionality. I'm tinkering with that now. In the meantime I've also added a bug here: https://connect.microsoft.com/VisualStudio/feedback/details/1524749/inheriting-from-moneypunct-requires-use-of-unavailable-construction-information


回答1:


The fact is, do_decimal_place and do_thousands_place are respected by get_money. The difficulty is in the fact that the moneypunct that is being inherited from is being default constructed, so the supporting information to direct get_money to call do_decimal_place and do_thousands_place is not being set up.

Visual Studio's implementation of moneypunct provides two public constructors:

  1. moneypunct()
  2. moneypunct(const _Locinfo& _Lobj, size_t _Refs = 0, bool _Isdef = false)

locale's constructor calls the 2nd moneypunct constructor. Creating a proper _Locinfo is the crux of the problem as that information seems to be implementation specific. The linked Visual Studio Bug requests a way to construct a functional moneypunct without access to implementation details. In lieu of this information all moneypunct fields must be cooked up.

Since this question is about extending an expected working moneypunct the easiest way to do that would be to use an assignment operator or copy constructor. Bad news: both of those are deleted. So punct_facet(const money_punct&) will need to be written internally implementing the behavior of a copy constructor. The values that need to be copied correspond to all the virtual functions that need to be overridden and by punct_facet. In the end your class will end up looking similar to this:

template <typename T>
class punct_facet : public T {
protected:
    typename T::string_type m_grouping;
    typename T::string_type m_curr_symbol;
    typename T::string_type m_positive_sign;
    typename T::string_type m_negative_sign;
    int m_frac_digits;
    typename T::pattern m_pos_format;
    typename T::pattern m_neg_format;

    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('.');
    }

    typename T::string_type do_grouping() const {
        return m_grouping;
    }

    typename T::string_type do_curr_symbol() const {
        return m_curr_symbol;
    }

    typename T::string_type do_positive_sign() const {
        return m_positive_sign;
    }

    typename T::string_type do_negative_sign() const {
        return m_negative_sign;
    }

    int do_frac_digits() const {
        return m_frac_digits;
    }

    typename T::pattern do_pos_format() const {
        return m_pos_format;
    }

    typename T::pattern do_neg_format() const {
        return m_neg_format;
    }
public:
    punct_facet(const T& defaultFacet) : m_grouping(defaultFacet.grouping()),
                                         m_curr_symbol(defaultFacet.curr_symbol()),
                                         m_positive_sign(defaultFacet.positive_sign()),
                                         m_negative_sign(defaultFacet.negative_sign()),
                                         m_frac_digits(defaultFacet.frac_digits()),
                                         m_pos_format(defaultFacet.pos_format()),
                                         m_neg_format(defaultFacet.neg_format()) {}
};

EDIT:

This solution is cross platform but it is also unsatisfactory, because all the members that had to be added to punct_facet already exist in moneypunct. I am not aware of a clean workaround for this fattening. A compiler specific hack is available here: https://stackoverflow.com/a/31454039/2642059

This would result in a punct_facet that looked more like this given that Visual Studio places the v-table pointer as the first item in the object layout:

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);
    }
};

Incidentally this implementation of punct_facet is not supported in Clang 3.6.0 but is supported in gcc 5.1.0: http://coliru.stacked-crooked.com/a/e4a1d88b560d6d1b



来源:https://stackoverflow.com/questions/31291004/locale-facet-constructor-ignored

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