String_numput<> facet not writing to string

I have written a String_numput<> facet that derives from the num_put<> facet, in order to write to a string.
The program is based on an example givenby Stroustrup:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// A num_put<> facet specialization that writes to a string
template<typename C>
class String_numput : public std::num_put<
                         C,
                         typename std::basic_string<C>::iterator>
{
public:
   String_numput() :
      /// this facet won't go into a locale;
      /// it has a manually controlled lifetime
      std::num_put<C, typename std::basic_string<C>::iterator> {1}
   {
   }
};



It is tested as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using namespace std;


string s {};


void test(long i,
          string& s,
          int pos)
{
   String_numput<char> f;

   /// Format i into s at position pos;
   /// use cout's formatting rules
   f.put(s.begin() + pos, cout, ' ', i);

   cout << s;
}


int main()
{
   test(4567.9, s, 0);
   
   cout << "completed" << endl;
}


http://coliru.stacked-crooked.com/a/f4e8386682471e7d


However, nothing is written to the string. The O/P is:


completed


What seems to be the problem here?

Thanks.
I have no previous experience with std::num_put so I'm not sure how you are supposed to use it but the problem seems to be that the string is not long enough to hold the data that is written to it. If you make the string longer before calling put you'll see that it is indeed writing to it.
To append to a container, use std::back_insert_iterator<> as the iterator type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <string>
#include <locale>
#include <iterator>

template < typename C, typename T = std::char_traits<C>, typename A = std::allocator<C> > // just making it more generic
struct String_numput : std::num_put< C, std::back_insert_iterator< std::basic_string<C,T,A> > >
{ String_numput() : std::num_put< C, std::back_insert_iterator< std::basic_string<C,T,A> > >{1} {} };

template < typename T > void put( T value, std::string& str )
{ String_numput<char>{}.put( std::back_inserter(str), std::cout, ' ', value ) ; }

void test( std::string& str )
{
    put( 12345.6789, str ) ;
    str += ' ' ;
    std::cout.width(15) ;
    put( 987654321LL, str ) ;
    str += ' ' ;
    put( true, str ) ;
    str += '\n' ;
}

int main()
{
    std::string str ;

    test(str) ;
    std::cout << std::fixed << std::boolalpha ;
    test(str) ;
    std::cout.imbue( std::locale( "de_DE.utf8" ) ) ;
    test(str) ;

    std::cout << str  ;
}

http://coliru.stacked-crooked.com/a/0a44ce24a5e48cc2
Thanks, Borges, for the reply. You are absolutely right.

From a design perspective, I have decided to create 2 num_put<> specializations:

1) String_numput<C> : will write a numeric to a string at a specified position. The string must be large enough to accommodate the numeric.

2) String_numapp<C> : will append a numeric to a string using a back_inserter<string>.

This design is analogous to that of iterators and inserters: an iterator can access a specific element and an inserter can insert an element.

Accordingly, I have String_numput<C>: (http://coliru.stacked-crooked.com/a/fb1dfbb4ebe7caa3)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// A num_put<> facet specialization that writes to a string
template<typename C>
class String_numput : public num_put<
                         C,
                         typename basic_string<C>::iterator>
{
public:
   String_numput() :
      /// this facet won't go into a locale;
      /// it has a manually controlled lifetime
      num_put<C, typename basic_string<C>::iterator> {1}
   {
   }
};



And here's String_numapp<C>: (http://coliru.stacked-crooked.com/a/1351d3a052c3022a)

1
2
3
4
5
6
7
8
9
10
11
12
13
template<typename C>
class String_numapp : public num_put<
                         C,
                         back_insert_iterator<basic_string<C>>>
{
public:
   String_numapp() :
      /// this facet won't go into a locale;
      /// it has a manually controlled lifetime
      num_put<C, back_insert_iterator<basic_string<C>>> {1}
   {
   }
};


Both have been tested satisfactorily at the above links.
However, I have a few minor queries about this:

1) In String_numput<C>, a space is not added between successive numerics, on coliru. A space is added with the same code in my offline g++4.8.1 version, however:

http://coliru.stacked-crooked.com/a/fb1dfbb4ebe7caa3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void test(float fl, int pos)
{
   /// Format fl into s starting at pos; 
   /// s contains sufficient space;
   /// use cout's formatting rules
   f.put(s.begin() + pos, cout, ' ', fl);

   PR(s); PRL;
}


/// Find true length of a string
/// ie, length of all chars till 1st space
int truelen(const string& s)
{
   auto pfirstsp = find(s.begin(), s.end(), '\0');
   
   return pfirstsp - s.begin();
}


int main()
{
   PRL; PRL;
   PRC("TestString_numput **");
   PRL;

   s.resize(20);
   test(4567.9, 0);
   test(1234.5, truelen(s) + 1);
}


The O/P is:

s = 4567.9
s = 4567.91234.5



2) String_numapp<> doesn't compile on coliru if the class declaration contains a "typename" prefix for the 2nd template argument. Offline on g++4.8, however, it compiles even with this.

1
2
3
4
5
6
7
8
9
10
11
12
13
template<typename C>
class String_numapp : public num_put<
                         C,
                         back_insert_iterator<basic_string<C>>>
{
public:
   String_numapp() :
      /// this facet won't go into a locale;
      /// it has a manually controlled lifetime
      num_put<C, back_insert_iterator<basic_string<C>>> {1}
   {
   }
};


When is the "typename" prefix required and when is it wrong? My understanding is it is required to make clear that the argument contains a typename.

Thanks.
> In String_numput<C>, a space is not added between successive numerics, on coliru. A space is added with the same code in my offline g++4.8.1 version

It probably is a difference in how a null character is represented on output.
On coliru, even with g++-4.8, nothing is sent to the output for a null character.

http://coliru.stacked-crooked.com/a/3d3fa9586dcbc8db



> When is the "typename" prefix required and when is it wrong?

A typename prefix is required for all dependent type names except for base-specifiers and mem-initializer-ids
EDIT: also, in class templates, except for names that refer to the current instantiation

A typename prefix may be used (even where it is not required) if the name of the type is a qualified name

A typename prefix may not be used where it is not required if the name of the type is an unqualified name

http://coliru.stacked-crooked.com/a/06eabc9a5eab31aa
Last edited on
Topic archived. No new replies allowed.