Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

convert to std::string #152

Closed
avral opened this issue Dec 21, 2021 · 8 comments
Closed

convert to std::string #152

avral opened this issue Dec 21, 2021 · 8 comments

Comments

@avral
Copy link

avral commented Dec 21, 2021

Greetings! Is it possible to convert uint to hex std::string ? I found the wr_string method, but how do I get a string from char*?

@ckormanyos
Copy link
Owner

ckormanyos commented Dec 22, 2021

Is it possible to convert uint to hex std::string ?

That is a good question.

C++ does not make it particularly easy to convert user-defined integral types to hexadecimal string representation. But it can be done.

In a first step, consider simply printing a math::wide_decimal::uint256_t to the console.

#include <iomanip>
#include <iostream>

#include <math/wide_integer/uintwide_t.h>

int main()
{
  using math::wide_integer::uint256_t;

  uint256_t u = 0x12345677U;

  u *= u;
  u *= u;
  u *= u;

  std::cout << std::hex << std::uppercase << std::showbase << u << std::endl;
  std::cout << std::hex << std::uppercase << std::showbase << (std::numeric_limits<uint256_t>::max)() << std::endl;
}

As a second step, some developers use string-streaming to convert to string. But you also mention that you'd like a hexadecimal representation.

As a second step, consider string-streaming. Look at the convenience template function hexlexical_cast shown below.

#include <iomanip>
#include <iostream>

#include <sstream>
#include <string>

#include <math/wide_integer/uintwide_t.h>

template<typename T>
std::string hexlexical_cast(const T& u)
{
  static_assert((std::numeric_limits<T>::is_specialized == true) && (std::numeric_limits<T>::is_signed == false),
                "Error: This convenience template function is intended for unsigned types.");

  std::stringstream strm;

  strm << std::hex << std::uppercase << std::showbase << u;

  return strm.str();
}

int main()
{
  using math::wide_integer::uint256_t;

  uint256_t u = 0x12345677U;

  u *= u;
  u *= u;
  u *= u;

  std::cout << hexlexical_cast(u) << std::endl;
  std::cout << hexlexical_cast((std::numeric_limits<uint256_t>::max)()) << std::endl;
}

Other variations are possible with different streaming flags, etc.

There is also std::to_string in the <string> library, but this is only for built-in integral types. it would be possible to create a namespace-specific version of this for uintwide_t and find it via ADL. But this has not been done at the moment.

There is also a facility called lexical_cast in the Boost C++ libraries, but it only casts to decimal representation.

The former idea is something that could potentially interest me. But for now, string-streaming might be a way to go...

@avral
Copy link
Author

avral commented Dec 22, 2021

Thank you very much for the time and help! The examples is great. will investigate it..

@avral avral closed this as completed Dec 22, 2021
@ckormanyos
Copy link
Owner

ckormanyos commented Dec 23, 2021

will investigate it

Before leaving this example, there is another thing I occasionally do when converting big integers. On embedded platforms the weight of string-streaming can be too heavy.

Although using raw pointers can be a bit, let's say, dangerous, I also occasionally use a self-written facility such as baselexical_cast. This can be particularly useful on resource-limited embedded systems.

An example thereof is shown below. It extracts digits in a given base one digit at a time until the input integer is reduced via division to zero. It's conceptually a little bit like std::to_chars.

An implementation of baselexical_cast is shown in the example below and can be found in its entirety in my microcontroller reference application project real-time-cpp in the file here.

#include <cstddef>
#include <cstdint>
#include <iomanip>
#include <iterator>
#include <iostream>
#include <sstream>
#include <string>

#include <math/wide_integer/uintwide_t.h>

namespace util {

template<typename UnsignedIntegerType,
          typename OutputIterator,
          const std::uint_fast8_t BaseRepresentation = 10U,
          const bool UpperCase = true>
OutputIterator baselexical_cast(const UnsignedIntegerType& u, OutputIterator OutFirst)
{
  using unsigned_integer_type = UnsignedIntegerType;
  using output_value_type     = typename std::iterator_traits<OutputIterator>::value_type;

  unsigned_integer_type x(u);

  std::ptrdiff_t index = 0;

  do
  {
    for(std::ptrdiff_t j = index; j >= 0; --j)
    {
      *(OutFirst + (j + 1)) = *(OutFirst + j);
    }

    ++index;

    output_value_type c = (output_value_type) (x % (unsigned_integer_type) BaseRepresentation);

    x = unsigned_integer_type(x / (unsigned_integer_type) BaseRepresentation);

    if(c <= (output_value_type) 9)
    {
      c = (output_value_type) (c + (output_value_type) '0');
    }
    else if((c >= (output_value_type) 0xA) && (c <= (output_value_type) 0xF))
    {
      c = (output_value_type) (  (output_value_type) (UpperCase ? (output_value_type) 'A' : (output_value_type) 'a')
                               + (output_value_type) (c - (output_value_type) 0xA));
    }

    *OutFirst = c;
  }
  while(x != 0U);

  return (OutputIterator) (OutFirst + index);
}

} // namespace util


int main()
{
  using math::wide_integer::uint256_t;

  {
    uint256_t u = 0x12345677U;

    u *= u;
    u *= u;
    u *= u;

    char pcr[128U] = { '\0' };

    util::baselexical_cast<uint256_t, char*, 16U>(u, pcr);

    std::cout << pcr << std::endl;
  }

  {
    char pcr[128U] = { '\0' };

    util::baselexical_cast<uint256_t, char*, 16U>((std::numeric_limits<uint256_t>::max)(), pcr);

    std::cout << pcr << std::endl;
  }
}

@johnmcfarlane
Copy link
Contributor

While it might not be easy to extract, this to_chars partial implementation will happily work with uintwide_t.

@ckormanyos
Copy link
Owner

partial implementation will happily work with uintwide_t

Thank you John. Sorry I had not previously noticed your particular implementation.

Do you also handle base(s) other than 10 such as the popular base-16?

@johnmcfarlane
Copy link
Contributor

Do you also handle base(s) other than 10 such as the popular base-16?

It doesn't but that should be pretty easy to implement. Might manage it over the holidays...

@ckormanyos
Copy link
Owner

ckormanyos commented Dec 24, 2021

Follow-up in #153

@johnmcfarlane
Copy link
Contributor

It wasn't nearly so easy as I thought but take a look back for a base parameter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants