3

I see how karma can be used to generate into a container that manages memory, like std::string. But what about the case where a buffer (char[N]) has been pre-allocated?

  {
    using namespace boost::spirit::karma;
    {
      std::string buffer;
      generate(std::inserter(buffer, buffer.begin()), double_, 3.13);
      std::cout << ':' << buffer << ':' << std::endl;
    }
    {
      //////////////////////////////////////////////////////////////////////
      // How to make the following work? Is there a builtin output
      // iterator that just works?
#if defined(MAJIC)
      char buffer[1024];
      generate(buffer, double_, 3.13);
      std::cout << ':' << buffer << ':' << std::endl;
#endif
    }
  }

I would like to find a way to parse the double into an address of an existing buffer. It is ok to assume the buffer is large enough for this case. Maybe the underlying question is really - is there already an output iterator adapter or something in karma for native arrays that could be used?

user1338952
  • 3,233
  • 2
  • 27
  • 41

1 Answers1

4

The Karma iterator-based API (here) takes... output iterators.

You can just create one for you array.

Problem with that is that you need to be very sure about the buffer capacity never being insufficient:

    char buffer[1024];
    char* it = buffer;
    karma::generate(it, karma::double_ << karma::eol, 3.13);

    std::cout.write(buffer, std::distance(buffer, it));

Note how you cannot assume the buffer to be NUL-terminated. Use the generated size.

Safe using array_sink:

There's a more convenient, more general approach in Boost Iostreams that is safe in the face of fixed-size buffers too:

    char buffer[310];
    io::stream<io::array_sink> as(buffer);
    boost::spirit::ostream_iterator it(as);

Here's a live demo that demonstrates the characteristics:

Live On Coliru

#include <boost/spirit/include/karma.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>

namespace karma = boost::spirit::karma;
namespace io    = boost::iostreams;

void test(std::vector<int> const& v) 
{
    char buffer[310];
    io::stream<io::array_sink> as(buffer);
    boost::spirit::ostream_iterator it(as);

    using namespace karma;
    if (generate(it, int_ % ", " << eol, v))
    {
        std::cout << "Success: ";
        std::cout.write(buffer, as.tellp());
    } else
        std::cout << "Generation failed (insufficient capacity?)\n";
}

int main() {

    std::cout << "Should be ok: \n";
    std::vector<int> v(100, 1);
    test(v);

    std::cout << "This will exceed buffer capacity: \n";
    std::iota(v.begin(), v.end(), 42);
    test(v);
}

Which prints

Should be ok: 
Success: 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
This will exceed buffer capacity: 
Generation failed (insufficient capacity?)
sehe
  • 374,641
  • 47
  • 450
  • 633
  • recorded [live stream](https://www.livecoding.tv/video/odr-karma-bzip2-iostreams-and-multi-index/) (after ~5 minutes) ([experiment](http://chat.stackoverflow.com/transcript/10?m=24182469#24182469)) – sehe Sep 30 '15 at 23:17
  • Can I contact you offline? I have a general question on Boost Karma's applicability to a situation I am running into (and for which the Q doesn't quite follow the stackoverflow pattern). – ForeverLearning Jun 01 '16 at 21:02
  • I addressed this to sehe using @ but for whatever reason SO is not displaying it, especially considering you have made a lone comment to your own post. – ForeverLearning Jun 01 '16 at 21:03
  • 1
    Thanks for the `array_sink` tip! – Chris Beck Apr 05 '18 at 15:32
  • @sehe Do you know how this actually works under the hood? I'm looking at `boost::spirit::ostream_iterator`, I assume karma detects that the buffer gets exhausted becuase of the `good` accessor here: https://github.com/boostorg/spirit/blob/develop/include/boost/spirit/home/karma/stream/ostream_iterator.hpp#L60 So karma is actually more general than just taking an `OutputIterator`, it will use the `good` member function if it is available to check if the output iterator is still valid? – Chris Beck Apr 05 '18 at 17:15
  • 1
    I guess the details are here actually, it makes sense now: https://github.com/boostorg/spirit/blob/develop/include/boost/spirit/home/karma/detail/output_iterator.hpp – Chris Beck Apr 05 '18 at 17:18