Skip to content

Commit ab015c6

Browse files
committed
Adding free format read functions
1 parent 52c75ea commit ab015c6

File tree

11 files changed

+697
-0
lines changed

11 files changed

+697
-0
lines changed

cmake/unit_testing.cmake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ add_subdirectory( src/tools/Log/test )
3030

3131
add_subdirectory( src/tools/disco/functions/test )
3232
add_subdirectory( src/tools/disco/BaseField/test )
33+
add_subdirectory( src/tools/disco/FreeFormatCharacter/test )
34+
add_subdirectory( src/tools/disco/FreeFormatInteger/test )
35+
add_subdirectory( src/tools/disco/FreeFormatReal/test )
3336
add_subdirectory( src/tools/disco/BaseFixedWidthField/test )
3437
add_subdirectory( src/tools/disco/Column/test )
3538
add_subdirectory( src/tools/disco/Character/test )

src/tools/disco.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#include "tools/disco/functions.hpp"
22
#include "tools/disco/BaseField.hpp"
3+
#include "tools/disco/FreeFormatCharacter.hpp"
4+
#include "tools/disco/FreeFormatInteger.hpp"
5+
#include "tools/disco/FreeFormatReal.hpp"
36
#include "tools/disco/BaseFixedWidthField.hpp"
47
#include "tools/disco/Column.hpp"
58
#include "tools/disco/Character.hpp"
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#ifndef NJOY_TOOLS_DISCO_INTEGER
2+
#define NJOY_TOOLS_DISCO_INTEGER
3+
4+
// system includes
5+
#include <cstdlib>
6+
#include <sstream>
7+
#include <iomanip>
8+
9+
// other includes
10+
#include "fast_float/fast_float.h"
11+
#include "tools/disco/BaseField.hpp"
12+
13+
namespace njoy {
14+
namespace tools {
15+
namespace disco {
16+
17+
/**
18+
* @brief A class for reading free format string values
19+
*/
20+
class FreeFormatCharacter : public BaseField {
21+
22+
/* fields */
23+
24+
public:
25+
26+
/**
27+
* @brief Read a string
28+
*
29+
* @param[in,out] iter an iterator to a character in a range
30+
* @param[in,out] end an iterator to the end of the range
31+
*/
32+
template < typename Representation, typename Iterator >
33+
static Representation read( Iterator& iter, const Iterator& end ) {
34+
35+
iter = std::find_if( iter, end,
36+
[] ( auto&& value )
37+
{ return ! std::isspace( value ); } );
38+
if ( isEndOfFile( iter ) ) {
39+
40+
throw std::runtime_error( "Cannot read valid string: end of file encountered" );
41+
}
42+
43+
auto temp = std::find_if( iter, end,
44+
[] ( auto&& value )
45+
{ return std::isspace( value ); } );
46+
Representation value( &*iter, temp - iter );
47+
iter = temp;
48+
return value;
49+
}
50+
};
51+
52+
} // disco namespace
53+
} // tools namespace
54+
} // njoy namespace
55+
56+
#endif
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
add_cpp_test( disco.FreeFormatCharacter FreeFormatCharacter.test.cpp )
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// include Catch2
2+
#include <catch2/catch_test_macros.hpp>
3+
4+
// what we are testing
5+
#include "tools/disco/FreeFormatCharacter.hpp"
6+
7+
// other includes
8+
9+
// convenience typedefs
10+
using namespace njoy::tools::disco;
11+
12+
SCENARIO( "Character" ) {
13+
14+
THEN( "strings can be read" ) {
15+
16+
std::string string;
17+
auto iter = string.begin();
18+
auto begin = string.begin();
19+
auto end = string.end();
20+
21+
string = "abcd";
22+
iter = string.begin();
23+
end = string.end();
24+
CHECK( "abcd" == FreeFormatCharacter::read< std::string >( iter, end ) );
25+
CHECK( iter == end );
26+
27+
string = "ab\n";
28+
iter = string.begin();
29+
end = string.end();
30+
CHECK( "ab" == FreeFormatCharacter::read< std::string >( iter, end ) );
31+
CHECK( iter == end - 1 );
32+
33+
string = " abcd";
34+
iter = string.begin();
35+
end = string.end();
36+
CHECK( "abcd" == FreeFormatCharacter::read< std::string >( iter, end ) );
37+
CHECK( iter == end );
38+
39+
string = " \n abcd";
40+
iter = string.begin();
41+
end = string.end();
42+
CHECK( "abcd" == FreeFormatCharacter::read< std::string >( iter, end ) );
43+
CHECK( iter == end );
44+
45+
string = " \r\n abcd";
46+
iter = string.begin();
47+
end = string.end();
48+
CHECK( "abcd" == FreeFormatCharacter::read< std::string >( iter, end ) );
49+
CHECK( iter == end );
50+
} // THEN
51+
} // SCENARIO

src/tools/disco/FreeFormatInteger.hpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#ifndef NJOY_TOOLS_DISCO_INTEGER
2+
#define NJOY_TOOLS_DISCO_INTEGER
3+
4+
// system includes
5+
#include <cstdlib>
6+
#include <sstream>
7+
#include <iomanip>
8+
9+
// other includes
10+
#include "fast_float/fast_float.h"
11+
#include "tools/disco/BaseField.hpp"
12+
13+
namespace njoy {
14+
namespace tools {
15+
namespace disco {
16+
17+
/**
18+
* @brief A class for reading free format integer values
19+
*/
20+
class FreeFormatInteger : public BaseField {
21+
22+
/* fields */
23+
24+
protected:
25+
26+
using BaseField::skipPlusSign;
27+
28+
public:
29+
30+
/**
31+
* @brief Read an integer
32+
*
33+
* @param[in,out] iter an iterator to a character in a range
34+
* @param[in,out] end an iterator to the end of the range
35+
*/
36+
template < typename Representation, typename Iterator >
37+
static Representation read( Iterator& iter, const Iterator& end ) {
38+
39+
Representation value = 0;
40+
41+
iter = std::find_if( iter, end,
42+
[] ( auto&& value )
43+
{ return ! std::isspace( value ); } );
44+
if ( isEndOfFile( iter ) ) {
45+
46+
throw std::runtime_error( "Cannot read valid integer: end of file encountered" );
47+
}
48+
49+
// we are using fast_float::from_chars instead of std::from_chars since
50+
// not all standard c++ libraries implement the floating point version of
51+
// std::from_chars
52+
if ( *iter == '+' ) { ++iter; }
53+
auto result = fast_float::from_chars( &*iter, &*end, value );
54+
if ( result.ec == std::errc() ) {
55+
56+
auto advance = result.ptr - &*iter;
57+
iter += advance;
58+
}
59+
else {
60+
61+
auto temp = std::find_if( iter, end,
62+
[] ( auto&& value )
63+
{ return std::isspace( value ); } );
64+
std::string message( &*iter, temp - iter );
65+
message.insert( 0, "Could not read valid integer: \"" );
66+
message += '\"';
67+
throw std::runtime_error( message );
68+
}
69+
70+
return value;
71+
}
72+
};
73+
74+
} // disco namespace
75+
} // tools namespace
76+
} // njoy namespace
77+
78+
#endif
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
add_cpp_test( disco.FreeFormatInteger FreeFormatInteger.test.cpp )
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// include Catch2
2+
#include <catch2/catch_test_macros.hpp>
3+
4+
// what we are testing
5+
#include "tools/disco/FreeFormatInteger.hpp"
6+
7+
// other includes
8+
9+
// convenience typedefs
10+
using namespace njoy::tools::disco;
11+
12+
SCENARIO( "Integer" ) {
13+
14+
GIVEN ( "strings with data" ) {
15+
16+
THEN( "the strings are consumed up to the valid data" ) {
17+
18+
std::string string;
19+
auto iter = string.begin();
20+
auto begin = string.begin();
21+
auto end = string.end();
22+
23+
string = " 10 ";
24+
iter = string.begin();
25+
end = string.end();
26+
CHECK( 10 == FreeFormatInteger::read< int >( iter, end ) );
27+
CHECK( iter == end - 1 );
28+
29+
string = " \n 10 ";
30+
iter = string.begin();
31+
end = string.end();
32+
CHECK( 10 == FreeFormatInteger::read< int >( iter, end ) );
33+
CHECK( iter == end - 1 );
34+
35+
string = " \r\n 10 ";
36+
iter = string.begin();
37+
end = string.end();
38+
CHECK( 10 == FreeFormatInteger::read< int >( iter, end ) );
39+
CHECK( iter == end - 1 );
40+
41+
string = " \t 10 ";
42+
iter = string.begin();
43+
end = string.end();
44+
CHECK( 10 == FreeFormatInteger::read< int >( iter, end ) );
45+
CHECK( iter == end - 1 );
46+
47+
string = " +123";
48+
iter = string.begin();
49+
end = string.end();
50+
CHECK( 123 == FreeFormatInteger::read< int >( iter, end ) );
51+
CHECK( iter == end );
52+
53+
string = " 123";
54+
iter = string.begin();
55+
end = string.end();
56+
CHECK( 123 == FreeFormatInteger::read< int >( iter, end ) );
57+
CHECK( iter == end );
58+
59+
string = " -123";
60+
iter = string.begin();
61+
end = string.end();
62+
CHECK( -123 == FreeFormatInteger::read< int >( iter, end ) );
63+
CHECK( iter == end );
64+
65+
string = "+123 ";
66+
iter = string.begin();
67+
end = string.end();
68+
CHECK( 123 == FreeFormatInteger::read< int >( iter, end ) );
69+
CHECK( iter == begin + 4 );
70+
71+
string = "123 ";
72+
iter = string.begin();
73+
end = string.end();
74+
CHECK( 123 == FreeFormatInteger::read< int >( iter, end ) );
75+
CHECK( iter == begin + 3 );
76+
77+
string = "-123 ";
78+
iter = string.begin();
79+
end = string.end();
80+
CHECK( -123 == FreeFormatInteger::read< int >( iter, end ) );
81+
CHECK( iter == begin + 4 );
82+
83+
string = " +123 ";
84+
iter = string.begin();
85+
end = string.end();
86+
CHECK( 123 == FreeFormatInteger::read< int >( iter, end ) );
87+
CHECK( iter == begin + 7 );
88+
89+
string = " 123 ";
90+
iter = string.begin();
91+
end = string.end();
92+
CHECK( 123 == FreeFormatInteger::read< int >( iter, end ) );
93+
CHECK( iter == begin + 6 );
94+
95+
string = " -123 ";
96+
iter = string.begin();
97+
end = string.end();
98+
CHECK( -123 == FreeFormatInteger::read< int >( iter, end ) );
99+
CHECK( iter == begin + 7 );
100+
101+
string = " +123\n";
102+
iter = string.begin();
103+
end = string.end();
104+
CHECK( 123 == FreeFormatInteger::read< int >( iter, end ) );
105+
CHECK( iter == end - 1 );
106+
107+
string = " +123";
108+
string += char{ std::char_traits<char>::eof() };
109+
iter = string.begin();
110+
end = string.end();
111+
CHECK( 123 == FreeFormatInteger::read< int >( iter, end ) );
112+
CHECK( iter == end - 1 );
113+
} // THEN
114+
} // GIVEN
115+
116+
GIVEN ( "strings with invalid data" ) {
117+
118+
THEN( "the strings are consumed up to the invalid data and an "
119+
"exception is thrown" ) {
120+
121+
std::string string;
122+
auto iter = string.begin();
123+
auto begin = string.begin();
124+
auto end = string.end();
125+
126+
string = " ";
127+
iter = string.begin();
128+
end = string.end();
129+
CHECK_THROWS( FreeFormatInteger::read< int >( iter, end ) );
130+
CHECK( iter == end );
131+
132+
string = " \n ";
133+
iter = string.begin();
134+
end = string.end();
135+
CHECK_THROWS( FreeFormatInteger::read< int >( iter, end ) );
136+
CHECK( iter == end );
137+
138+
string = " \r\n ";
139+
iter = string.begin();
140+
end = string.end();
141+
CHECK_THROWS( FreeFormatInteger::read< int >( iter, end ) );
142+
CHECK( iter == end );
143+
144+
string = " \t ";
145+
iter = string.begin();
146+
end = string.end();
147+
CHECK_THROWS( FreeFormatInteger::read< int >( iter, end ) );
148+
CHECK( iter == end );
149+
150+
string = " abc ";
151+
iter = string.begin();
152+
end = string.end();
153+
CHECK_THROWS( FreeFormatInteger::read< int >( iter, end ) );
154+
CHECK( iter == begin + 2 );
155+
} // THEN
156+
} // GIVEN
157+
} // SCENARIO

0 commit comments

Comments
 (0)