LvArray
input.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2021, Lawrence Livermore National Security, LLC and LvArray contributors.
3  * All rights reserved.
4  * See the LICENSE file for details.
5  * SPDX-License-Identifier: (BSD-3-Clause)
6  */
7 
13 #pragma once
14 
15 // Source includes
16 #include "Array.hpp"
17 
18 // System includes
19 #include <string>
20 #include <iostream>
21 
22 namespace LvArray
23 {
24 
28 namespace input
29 {
30 namespace internal
31 {
32 
37 template< typename T, typename INDEX_TYPE >
39 {
40 
45  static void skipDelimiters( std::istringstream & inputStream )
46  {
47  while( inputStream.peek() == ' ' )
48  {
49  inputStream.ignore();
50  }
51  }
52 
58  template< int NDIM >
59  static void Read( T & arrayValue,
60  INDEX_TYPE const *,
61  std::istringstream & inputStream )
62  {
63  inputStream >> arrayValue;
64 
65  LVARRAY_THROW_IF( inputStream.fail(),
66  "Invalid value of type " << typeid(T).name() << " in: " <<
67  ( inputStream.eof() ? "" : inputStream.str().substr( inputStream.tellg() ) ),
68  std::invalid_argument );
69  }
70 
77  template< int NDIM, int USD >
78  static void
80  INDEX_TYPE const * const dims,
81  std::istringstream & inputStream )
82  {
83  LVARRAY_THROW_IF( inputStream.peek() != '{', "Opening '{' not found for input array: "<<inputStream.str(),
84  std::invalid_argument );
85  inputStream.ignore();
86 
87  for( int i=0; i<(*dims); ++i )
88  {
89  skipDelimiters( inputStream );
90  Read< NDIM-1 >( arraySlice[i], dims+1, inputStream );
91  }
92 
93  skipDelimiters( inputStream );
94  LVARRAY_THROW_IF( inputStream.peek() != '}', "Closing '}' not found for input array: "<<inputStream.str(),
95  std::invalid_argument );
96  inputStream.ignore();
97  }
98 };
99 
100 } // namespace internal
101 
119 template< typename T,
120  int NDIM,
121  typename PERMUTATION,
122  typename INDEX_TYPE,
123  template< typename > class BUFFER_TYPE >
125  std::string valueString )
126 {
127  // Check to make sure there are no space delimited values. The assumption is anything that is not
128  // a '{' or '}' or ',' or ' ' is part of a value. Loope over the string and keep track of whether
129  // or not there is a value on the left of the char. If there is a value to the left, with a space
130  // on the left, and we run into another value, then this means that there is a space delimited
131  // entry.
132  {
133  bool valueOnLeft = false;
134  bool spaceOnLeft = false;
135 
136  for( char const c : valueString )
137  {
138  if( c != '{' && c != ',' && c != '}' && c!=' ' )
139  {
140  if( valueOnLeft && spaceOnLeft )
141  {
142  LVARRAY_THROW( "Array value sequence specified without ',' delimiter: "<<valueString,
143  std::invalid_argument );
144  }
145  }
146 
147  // If a open/close brace or ',' delimiter, then there is neither a value or space
148  // to the left of subsequent characters.
149  if( c == '{' || c == ',' || c == '}' )
150  {
151  valueOnLeft = false;
152  spaceOnLeft = false;
153  }
154  else // if the first check fails, then either c is a space or value
155  {
156  // if it is a value, then set the valueOnLeft flag to true for subsequent c
157  if( c != ' ' )
158  {
159  valueOnLeft = true;
160  spaceOnLeft = false;
161  }
162  else // if it is a space, then set spaceOnLeft to true for subsequent c
163  {
164  spaceOnLeft = true;
165  }
166  }
167  }
168  }
169 
170  // erase all spaces from input string to simplify parsing
171  valueString.erase( std::remove( valueString.begin(), valueString.end(), ' ' ), valueString.end());
172 
173  // allow for a null input
174  if( valueString=="{}" )
175  {
176  array.clear();
177  return;
178  }
179 
180  // checking for various formatting errors
181  LVARRAY_THROW_IF( valueString.find( "}{" ) != std::string::npos,
182  "Sub arrays not separated by ',' delimiter: "<<valueString,
183  std::invalid_argument );
184 
185  LVARRAY_THROW_IF( valueString[0]!='{',
186  "First non-space character of input string for an array must be '{'. Given string is: \n"<<valueString,
187  std::invalid_argument );
188 
189  size_t const numOpen = std::count( valueString.begin(), valueString.end(), '{' );
190  size_t const numClose = std::count( valueString.begin(), valueString.end(), '}' );
191 
192  LVARRAY_THROW_IF( numOpen != numClose,
193  "Number of opening '{' not equal to number of '}' in processing of string for filling"
194  " an Array. Given string is: \n"<<valueString,
195  std::invalid_argument );
196 
197 
198  // after allowing for the null input, disallow a sub-array null input
199  LVARRAY_THROW_IF( valueString.find( "{}" )!=std::string::npos,
200  "Cannot have an empty sub-dimension of an array, i.e. { { 0, 1}, {} }. "
201  "The input is"<<valueString,
202  std::invalid_argument );
203 
204  // get the number of dimensions from the number of { characters that begin the input string
205  int const ndims = LvArray::integerConversion< int >( valueString.find_first_not_of( '{' ));
206  LVARRAY_THROW_IF( ndims!=NDIM,
207  "number of dimensions in string ("<<ndims<<
208  ") does not match dimensions of array("<<NDIM<<
209  "). String is:/n"<<valueString,
210  std::invalid_argument );
211 
212 
213  // now get the number of dimensions, and the size of each dimension.
214 
215  // use dimLevel to track the current dimension we are parsing
216  int dimLevel = -1;
217 
218  // dims is the dimensions that get set the first diving down.
219  INDEX_TYPE dims[NDIM] = {0};
220 
221  // currentDims is used to track the dimensions for subsequent dives down the dimensions.
222  INDEX_TYPE currentDims[NDIM] = {0};
223 
224  // flag to see if the dims value has been set for a given dimension
225  bool dimSet[NDIM] = {false};
226 
227  for( int i=0; i<NDIM; ++i )
228  {
229  dims[i]=1;
230  currentDims[i] = 1;
231  }
232 
233  char lastChar = 0;
234  for( size_t charCount = 0; charCount<valueString.size(); ++charCount )
235  {
236  char const c = valueString[charCount];
237  // this had better be true for the first char...we had a check for this. This is why we can
238  // set dimLevel = -1 to start.
239  if( c=='{' )
240  {
241  ++dimLevel;
242  }
243  else if( c=='}' )
244  {
245  LVARRAY_THROW_IF( lastChar==',',
246  "character '}' follows '"<<lastChar<<"'. Closing brace must follow an array value.",
247  std::invalid_argument );
248 
249  // } means that we are closing a dimension. That means we know the size of this dimLevel
250  dimSet[dimLevel] = true;
251  LVARRAY_THROW_IF( dims[dimLevel]!=currentDims[dimLevel],
252  "Dimension "<<dimLevel<<" is inconsistent across the expression. "
253  "The first set value of the dimension is "<<dims[dimLevel]<<
254  " while the current value of the dimension is"<<currentDims[dimLevel]<<
255  ". The values that have been parsed prior to the error are:\n"<<
256  valueString.substr( 0, charCount+1 ),
257  std::invalid_argument );
258 
259  // reset currentDims and drop dimLevel for post-closure parsing
260  currentDims[dimLevel] = 1;
261  --dimLevel;
262  LVARRAY_THROW_IF( dimLevel<0 && charCount<(valueString.size()-1),
263  "In parsing the input string, the current dimension of the array has dropped "
264  "below 0. This means that there are more '}' than '{' at some point in the"
265  " parsing. The values that have been parsed prior to the error are:\n"<<
266  valueString.substr( 0, charCount+1 ),
267  std::invalid_argument );
268 
269  }
270  else if( c==',' ) // we are counting the dimension sizes because there is a delimiter.
271  {
272  LVARRAY_THROW_IF( lastChar=='{' || lastChar==',',
273  "character of ',' follows '"<<lastChar<<"'. Comma must follow an array value.",
274  std::invalid_argument );
275  if( dimSet[dimLevel]==false )
276  {
277  ++(dims[dimLevel]);
278  }
279  ++(currentDims[dimLevel]);
280  }
281  lastChar = c;
282  }
283  LVARRAY_THROW_IF( dimLevel!=-1,
284  "Expression fails to close all '{' with a corresponding '}'. Check your input:"<<
285  valueString,
286  std::invalid_argument );
287 
288  array.resize( NDIM, dims );
289 
290 
291  // we need to replace our ',' with ' ' for reading in an array of strings, otherwise the
292  // stringstream::operator>> will grab the ','
293  std::replace( valueString.begin(), valueString.end(), ',', ' ' );
294 
295  // we also need to add a ' ' in front of any '}' otherwise the
296  // stringstream::operator>> will grab the }
297  for( std::string::size_type a=0; a<valueString.size(); ++a )
298  {
299  if( valueString[a] == '}' )
300  {
301  valueString.insert( a, " " );
302  ++a;
303  }
304  }
305  std::istringstream strstream( valueString );
306  // this recursively reads the values from the stringstream
308 }
309 
310 } // namespace input
311 } // namespace LvArray
void clear()
Sets the size of the Array to zero and destroys all the values.
Definition: Array.hpp:466
A helper struct to recursively read an istringstream into an array.
Definition: input.hpp:38
This class serves to provide a sliced multidimensional interface to the family of LvArray classes...
Definition: ArraySlice.hpp:89
DISABLE_HD_WARNING LVARRAY_HOST_DEVICE bool remove(T *const LVARRAY_RESTRICT ptr, std::ptrdiff_t const size, T const &value, CALLBACKS &&callBacks)
Remove the given value from the array if it exists.
Definition: sortedArrayManipulation.hpp:473
static void stringToArray(Array< T, NDIM, PERMUTATION, INDEX_TYPE, BUFFER_TYPE > &array, std::string valueString)
This function reads the contents of a string into an Array.
Definition: input.hpp:124
LVARRAY_HOST_DEVICE constexpr ArraySlice< T, NDIM, USD, INDEX_TYPE > toSlice() const &noexcept
Definition: ArrayView.hpp:308
#define LVARRAY_THROW_IF(EXP, MSG, TYPE)
Conditionally throw an exception.
Definition: Macros.hpp:201
static void skipDelimiters(std::istringstream &inputStream)
function to skip &#39;,&#39; delimiters in a istringstream
Definition: input.hpp:45
Contains the implementation of LvArray::Array.
static void Read(T &arrayValue, INDEX_TYPE const *, std::istringstream &inputStream)
Reads a value of the array from the stream.
Definition: input.hpp:59
#define LVARRAY_THROW(MSG, TYPE)
Throw an exception.
Definition: Macros.hpp:220
The top level namespace.
Definition: Array.hpp:24
LVARRAY_HOST_DEVICE void resize(int const numDims, DIMS_TYPE const *const dims)
Resize the dimensions of the Array to match the given dimensions.
Definition: Array.hpp:320
static void Read(ArraySlice< T, NDIM, USD, INDEX_TYPE > const arraySlice, INDEX_TYPE const *const dims, std::istringstream &inputStream)
Recursively read values from an istringstream into an array.
Definition: input.hpp:79
LVARRAY_HOST_DEVICE constexpr INDEX_TYPE const * dims() const noexcept
Definition: ArrayView.hpp:470
This class provides a fixed dimensional resizeable array interface in addition to an interface simila...
Definition: Array.hpp:55