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
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:
moneypunct()
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