Skip to content

Commit

Permalink
Extend number format (#19)
Browse files Browse the repository at this point in the history
Add custom decimal and thousands separarators
  • Loading branch information
DavidvanErkelens authored Mar 26, 2020
1 parent 10df6a7 commit 1bd1ec1
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 17 deletions.
76 changes: 59 additions & 17 deletions src/builtin/number_format.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
* Built-in "|number_format" modifier
*
* @author David van Erkelens <[email protected]>
* @copyright 2019 Copernica BV
* @copyright 2019 - 2020 Copernica BV
*/

/**
* Dependencies
*/
#include <locale>

/**
* Namespace
*/
Expand All @@ -17,6 +22,25 @@ namespace SmartTpl { namespace Internal {
*/
class NumberFormatModifier : public Modifier
{
private:
/**
* Struct that works with the std::numpunct facet and can be
* constructed with a char for the decimal and thousands separators
*/
struct formatter : std::numpunct<char>
{
// chars for the separators
char decimal; char thousand;

// constructor
formatter(char decimal, char thousand) : decimal(decimal), thousand(thousand) {}

// build in functions for separator formatting
char do_thousands_sep() const { return thousand; } // thousands separator
std::string do_grouping() const { return (int) thousand == 0 ? "" : "\3"; } // separate every 3 digits, if we have a separator
char do_decimal_point() const { return decimal; } // decimal separator
};

public:
/**
* Destructor
Expand All @@ -31,32 +55,50 @@ class NumberFormatModifier : public Modifier
*/
VariantValue modify(const Value &input, const SmartTpl::Parameters &params) override
{
// Convert input to double
double original(input.toDouble());

// make sure we have a parameter containing the format
// make sure we have a parameter containing the number of decimals
if (params.size() < 1) throw NoModification();

// get the amound of decimals to output
// get the amount of decimals to output
int decimals = params[0].toInteger();

// buffer to create the printf format
char format[10];
// get separator variables for decimals and thousands
char decimal_separator = '.'; char thousand_separator = (char) 0;

// if we have a valid parameter, overwrite decimal separator
if (params.size() > 1)
{
// convert to string
auto param = params[1].toString();

// make sure that we have a character (we could throw if the param is too long?)
if (param.size() > 0) decimal_separator = param[0];
}

// if we have a valid parameter, overwrite thousands separator
if (params.size() > 2)
{
// convert to string
auto param = params[2].toString();

// make sure that we have a character (we could throw if the param is too long?)
if (param.size() > 0) thousand_separator = param[0];
}

// format the format
sprintf(format, "%%.%if", decimals);
// create stringstream to store formatted number
std::stringstream stream;

// calculate size of new string
size_t size = snprintf(nullptr, 0, format, original);
// create custom locale for our formatting options
std::locale formatting_locale(stream.getloc(), new NumberFormatModifier::formatter(decimal_separator, thousand_separator));

// create buffer
char buffer[size + 1];
// set formatting options
stream.precision(decimals);
stream.imbue(formatting_locale);

// create new string
sprintf(buffer, format, original);
// stream the value (never in scientific format)
stream << std::fixed << input.toDouble();

// create object
return VariantValue(buffer);
return VariantValue(stream.str());
}
};

Expand Down
19 changes: 19 additions & 0 deletions test/modifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,25 @@ TEST(Modifier, NumberFormat)
}
}

TEST(Modifier, NumberFormatWithSeparators)
{
string input("{$var1|number_format:2:'.':','}\n{$var1|number_format:2:',':'|'}\n{$var1|number_format:0:'.':','}\n{$var1|number_format:2:'x'}");
Template tpl((Buffer(input)));

Data data;
data.assign("var1", "123123.45");

string expectedOutput("123,123.45\n123|123,45\n123,123\n123123x45");

EXPECT_EQ(expectedOutput, tpl.process(data));

if (compile(tpl)) // This will compile the Template into a shared library
{
Template library(File(SHARED_LIBRARY)); // Here we load that shared library
EXPECT_EQ(expectedOutput, library.process(data));
}
}

TEST(Modifier, Count)
{
string input("{$var|count}");
Expand Down

0 comments on commit 1bd1ec1

Please sign in to comment.