1

So typically if I wanted to insert locale appropriate separators in some number, foo, I'd do something like this:

ostringstream out;

out.imbue(locale("en-US"));
out << foo;

Then I could just use out.str() as the separated string: http://coliru.stacked-crooked.com/a/054e927de25b5ad0

Unfortunately I've been asked not to use stringstreams in my current project. Is there any other way I can accomplish this? Ideally a locale dependent way?

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • So you want to convert a number to a thousand separated string or a string to a thousands separated string or do you just need to output it that way? – NathanOliver Jun 12 '17 at 14:37
  • @NathanOliver Yeah, and obviously I could manually insert them. But that's not going to be locale appropriate :( – Jonathan Mee Jun 12 '17 at 14:39
  • Can't you just use [this](https://stackoverflow.com/questions/17530408/print-integer-with-thousands-and-millions-separator) then? unless you actually need a string. – NathanOliver Jun 12 '17 at 14:41
  • @NathanOliver Ummm... both the answers here are using an `ostream`? So no, I can't do that. – Jonathan Mee Jun 12 '17 at 14:47
  • What I'm try to ask, unsucessfully so far, is how do you need this number with the thousands separator? Do you need it as a `std::string` or do you just need to output it? If you just need to output iit then you set the local on the output stream. If you actually need a `std::string` something else will need to be done. What is it that you need? – NathanOliver Jun 12 '17 at 14:50
  • @NathanOliver I see, I'm sorry. Yes I need it as something I can store, a `string` being ideal, but a `char*` would work if I must manage my own string. – Jonathan Mee Jun 12 '17 at 15:00
  • 1
    Looks like you can use [this](https://stackoverflow.com/a/11695246/4342498) but use `sprintf` instead of `printf`. – NathanOliver Jun 12 '17 at 15:10
  • @NathanOliver I feel very uncomfortable with the warning that creates: http://coliru.stacked-crooked.com/a/39b83637096e554d can you demonstrate that the C++ standard allows this? – Jonathan Mee Jun 12 '17 at 15:19
  • 1
    Looks like that is only a C feature. Bummer. – NathanOliver Jun 12 '17 at 15:21
  • 3
    Not using `stringstream` is a bizarre requirement. Especially if you must also handle locales. – Omnifarious Jun 12 '17 at 15:29
  • @Omnifarious I'm with you on that 100%. – Jonathan Mee Jun 12 '17 at 15:32
  • @NathanOliver I didn't see it on first pass, but [Jerry Coffin's Answer](https://stackoverflow.com/a/5346394/2642059) to that question contained a C solution to this. I've moved his solution to C++ in my answer: https://stackoverflow.com/a/44549637/2642059 Thanks for the tip. – Jonathan Mee Jun 14 '17 at 16:19

2 Answers2

1

So this answer is the C++ distillation of Jerry Coffin's answer to this question: Cross Platform Support for sprintf's Format '-Flag

template <typename T>
enable_if_t<is_integral_v<remove_reference_t<T>>, string> poscommafmt(const T N, const numpunct<char>& fmt_info) {
    const auto group = fmt_info.grouping();
    auto posn = cbegin(group);
    auto divisor = static_cast<T>(pow(10.0F, static_cast<int>(*posn)));
    auto quotient = div(N, divisor);
    auto result = to_string(quotient.rem);

    while(quotient.quot > 0) {
        if(next(posn) != cend(group)) {
            divisor = static_cast<T>(pow(10.0F, static_cast<int>(*++posn)));
        }
        quotient = div(quotient.quot, divisor);
        result = to_string(quotient.rem) + fmt_info.thousands_sep() + result;
    }
    return result;
}

template <typename T>
enable_if_t<is_integral_v<remove_reference_t<T>>, string> commafmt(const T N, const numpunct<char>& fmt_info) {
    return N < 0 ? '-' + poscommafmt(-N, fmt_info) : poscommafmt(N, fmt_info);
}

Naturally this suffers from the identical 2's compliment negation issue.

This certainly benefits from C++'s string memory management, but also from the ability to pass in a specific numpunct<char> which need not be the current locale. For example whether or not cout.getloc() == locale("en-US") you can call: commafmt(foo, use_facet<numpunct<char>>(locale("en-US")))

Live Example

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
0

Set up a pipe. Use the linked to code to construct an ofstream and ifstream from a file descriptor and then output to one and read from the other.

This is a bizarre and contorted solution. But then the idea that you have to use locales, have to store things, and can't use stringstream is just as bizarre. So, they give you bizarre requirements, they get bizarre code.

Omnifarious
  • 54,333
  • 19
  • 131
  • 194
  • 1
    Haha, I can guarantee this won't make it past review :) – Jonathan Mee Jun 12 '17 at 15:41
  • 1
    @JonathanMee - Then you can ask them for a better way to satisfy requirements. :-) Sometimes, this is the best way to force stupid decisions to get the scrutiny they deserve. I've told the testing department what test cases to put in to trigger bugs I knew were there but the developer wouldn't acknowledge. Sometimes it's the organization and not the code that you have to hack. – Omnifarious Jun 12 '17 at 15:49
  • That is probably a reasonable suggestion. By the time writing a separate program to solve the problem is gettign thrown around as a solution you know things have gotten pretty bad. – Jonathan Mee Jun 12 '17 at 16:01
  • @JonathanMee - May I ask what the rationale is for avoiding `stringstream`? – Omnifarious Jun 12 '17 at 16:02
  • C++'s streams were called out as being do not use in the coding standard I'm working under, for obvious reasons exceptions are being made now. I couldn't find a work around so I asked on here as a last ditch effort. If no one has any other ideas I'll be pointing to this post as proof that there's not a workaround and getting an exception. – Jonathan Mee Jun 12 '17 at 16:32
  • 1
    @JonathanMee - I think you did not answer Omnifarious last question. Could you try again: Does the coding standard say why 'do not use streams'? Does anyone remember the rationale offered for not using streams? – 2785528 Jun 13 '17 at 02:28
  • @DOUGLASO.MOEN So I asked around about this. I'll relate to you my understanding of what was explained to me: The original issue came from the implementation of `stringstream` on PS2/PS3 where there was a significant bug in the implementation. In the current gen The statement is that it is simply disproportionately heavy. My next step is going to be to inspect gcc's implementation of `sprintf`'s format `'`-width flag. If that's something I can leverage cross platform I'll post an answer here. – Jonathan Mee Jun 13 '17 at 13:14
  • @JonathanMee - thanks. I think there were lots of bugs in STL back when I started C++, but what did I know? hmmm The stuff is much better now. – 2785528 Jun 14 '17 at 00:35
  • @JonathanMee - I'm now tempted to write a test to generate a very large number of random numbers and format them with `stringstream` and then with `sprintf` and see which is faster. If I do, I will be assigning the buffer passed to `sprintf` to a `::std::string` to make it fair because you said you have to store these things. – Omnifarious Jun 14 '17 at 00:37
  • @JonathanMee - It turns out that C++ has some string conversion functions that are locale aware that you might want to use: http://en.cppreference.com/w/cpp/string/basic_string/to_string – Omnifarious Jun 15 '17 at 02:38
  • @Omnifarious Those functions are locale aware in the same way that the underlying `sprintf` statement is locale aware. But unlike C++'s streams the standard provided formaters didn't allow for a way to comma separate the number ([SUSv2 did](https://stackoverflow.com/questions/44523855/cross-platform-support-for-sprintfs-format-flag?noredirect=1&lq=1), but that's non-standard.) So unfortunately `to_string` won't give me what I want. – Jonathan Mee Jun 15 '17 at 13:40