keith keith - 2 months ago 15
C++ Question

Override more than one facet on std::locale

Is it possible to override more than one facet on

std::local
?

For instance, taking a rather contrived example:

#include <locale>

class NumberFacet : public std::numpunct<char>
{
protected:
char do_decimal_point() const override
{
return '.';
}

char do_thousands_sep() const override
{
return ',';
}
};

class MoneyFacet : public std::moneypunct<char>
{
protected:
char do_decimal_point() const override
{
return '.';
}

char do_thousands_sep() const override
{
return ',';
}
};


I know I can override one facet of
std::locale
like this to create a new
locale
variable.

std::locale locale(std::locale(), new NumberFacet());


How would I pass
MoneyFacet
as well?

It seems unsatisfactory that I would have to do this:

std::locale locale(std::locale(std::locale(), new NumberFacet()), new MoneyFacet());


Is there a nicer way?

Answer Source

The IOStreams library doesn't provide you with a better way of writing that, but you can take advantage of recursion to get the job done. Since imbuing a new locale always involves copying from an old locale, you can continuously recurse to build a new locale with the provided facets.

template<class...>struct types{constexpr types(){}};
template<class...Ts>constexpr types<Ts...> types_t{};

template<class L, class F>
L makeloc(L loc, types<F>, int) {
    return std::locale(loc, new F{});
}

template<class L, class F, class... Fs>
L makeloc(L loc, types<F, Fs...>, long) {
    return makeloc(std::locale(loc, new F{}), types_t<Fs...>, 0);
}

template<class S, class L, class... Fs>
void imbue(S& s, L loc, types<Fs...> facets) {
    s.imbue(makeloc(loc, facets, 0));
}

int main() {
    imbue(std::cout, std::locale(), types_t<MoneyFacet, NumberFacet /*, ... */>);
}