GEOSX
input.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020, 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 >
38 struct StringToArrayHelper
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_ERROR_IF( inputStream.fail(),
66  "Invalid value of type " << typeid(T).name() << " in: " <<
67  ( inputStream.eof() ? "" : inputStream.str().substr( inputStream.tellg() ) ) );
68  }
69 
76  template< int NDIM, int USD >
77  static void
78  Read( ArraySlice< T, NDIM, USD, INDEX_TYPE > const arraySlice,
79  INDEX_TYPE const * const dims,
80  std::istringstream & inputStream )
81  {
82  LVARRAY_ERROR_IF( inputStream.peek() != '{', "opening { not found for input array: "<<inputStream.str() );
83  inputStream.ignore();
84 
85  for( int i=0; i<(*dims); ++i )
86  {
87  skipDelimiters( inputStream );
88  Read< NDIM-1 >( arraySlice[i], dims+1, inputStream );
89  }
90 
91  skipDelimiters( inputStream );
92  LVARRAY_ERROR_IF( inputStream.peek() != '}', "closing } not found for input array: "<<inputStream.str() );
93  inputStream.ignore();
94  }
95 };
96 
97 } // namespace internal
98 
116 template< typename T,
117  int NDIM,
118  typename PERMUTATION,
119  typename INDEX_TYPE,
120  template< typename > class BUFFER_TYPE >
122  std::string valueString )
123 {
124  // Check to make sure there are no space delimited values. The assumption is anything that is not
125  // a '{' or '}' or ',' or ' ' is part of a value. Loope over the string and keep track of whether
126  // or not there is a value on the left of the char. If there is a value to the left, with a space
127  // on the left, and we run into another value, then this means that there is a space delimited
128  // entry.
129  {
130  bool valueOnLeft = false;
131  bool spaceOnLeft = false;
132 
133  for( char const c : valueString )
134  {
135  if( c != '{' && c != ',' && c != '}' && c!=' ' )
136  {
137  if( valueOnLeft && spaceOnLeft )
138  {
139  LVARRAY_ERROR( "Array value sequence specified without ',' delimiter: "<<valueString );
140  }
141  }
142 
143  // If a open/close brace or ',' delimiter, then there is neither a value or space
144  // to the left of subsequent characters.
145  if( c == '{' || c == ',' || c == '}' )
146  {
147  valueOnLeft = false;
148  spaceOnLeft = false;
149  }
150  else // if the first check fails, then either c is a space or value
151  {
152  // if it is a value, then set the valueOnLeft flag to true for subsequent c
153  if( c != ' ' )
154  {
155  valueOnLeft = true;
156  spaceOnLeft = false;
157  }
158  else // if it is a space, then set spaceOnLeft to true for subsequent c
159  {
160  spaceOnLeft = true;
161  }
162  }
163  }
164  }
165 
166  // erase all spaces from input string to simplify parsing
167  valueString.erase( std::remove( valueString.begin(), valueString.end(), ' ' ), valueString.end());
168 
169  // allow for a null input
170  if( valueString=="{}" )
171  {
172  array.clear();
173  return;
174  }
175 
176  // checking for various formatting errors
177  LVARRAY_ERROR_IF( valueString.find( "}{" ) != std::string::npos,
178  "Sub arrays not separated by ',' delimiter: "<<valueString );
179 
180  LVARRAY_ERROR_IF( valueString[0]!='{',
181  "First non-space character of input string for an array must be {. Given string is: \n"<<valueString );
182 
183  size_t const numOpen = std::count( valueString.begin(), valueString.end(), '{' );
184  size_t const numClose = std::count( valueString.begin(), valueString.end(), '}' );
185 
186  LVARRAY_ERROR_IF( numOpen != numClose,
187  "Number of opening { not equal to number of } in processing of string for filling"
188  " an Array. Given string is: \n"<<valueString );
189 
190 
191  // after allowing for the null input, disallow a sub-array null input
192  LVARRAY_ERROR_IF( valueString.find( "{}" )!=std::string::npos,
193  "Cannot have an empty sub-dimension of an array, i.e. { { 0, 1}, {} }. "
194  "The input is"<<valueString );
195 
196  // get the number of dimensions from the number of { characters that begin the input string
197  int const ndims = LvArray::integerConversion< int >( valueString.find_first_not_of( '{' ));
198  LVARRAY_ERROR_IF( ndims!=NDIM,
199  "number of dimensions in string ("<<ndims<<
200  ") does not match dimensions of array("<<NDIM<<
201  "). String is:/n"<<valueString );
202 
203 
204  // now get the number of dimensions, and the size of each dimension.
205 
206  // use dimLevel to track the current dimension we are parsing
207  int dimLevel = -1;
208 
209  // dims is the dimensions that get set the first diving down.
210  INDEX_TYPE dims[NDIM] = {0};
211 
212  // currentDims is used to track the dimensions for subsequent dives down the dimensions.
213  INDEX_TYPE currentDims[NDIM] = {0};
214 
215  // flag to see if the dims value has been set for a given dimension
216  bool dimSet[NDIM] = {false};
217 
218  for( int i=0; i<NDIM; ++i )
219  {
220  dims[i]=1;
221  currentDims[i] = 1;
222  }
223 
224  char lastChar = 0;
225  for( size_t charCount = 0; charCount<valueString.size(); ++charCount )
226  {
227  char const c = valueString[charCount];
228  // this had better be true for the first char...we had a check for this. This is why we can
229  // set dimLevel = -1 to start.
230  if( c=='{' )
231  {
232  ++dimLevel;
233  }
234  else if( c=='}' )
235  {
236  // } means that we are closing a dimension. That means we know the size of this dimLevel
237  dimSet[dimLevel] = true;
238  LVARRAY_ERROR_IF( dims[dimLevel]!=currentDims[dimLevel],
239  "Dimension "<<dimLevel<<" is inconsistent across the expression. "
240  "The first set value of the dimension is "<<dims[dimLevel]<<
241  " while the current value of the dimension is"<<currentDims[dimLevel]<<
242  ". The values that have been parsed prior to the error are:\n"<<
243  valueString.substr( 0, charCount+1 ) );
244 
245  // reset currentDims and drop dimLevel for post-closure parsing
246  currentDims[dimLevel] = 1;
247  --dimLevel;
248  LVARRAY_ERROR_IF( dimLevel<0 && charCount<(valueString.size()-1),
249  "In parsing the input string, the current dimension of the array has dropped "
250  "below 0. This means that there are more '}' than '{' at some point in the"
251  " parsing. The values that have been parsed prior to the error are:\n"<<
252  valueString.substr( 0, charCount+1 ) );
253 
254  }
255  else if( c==',' ) // we are counting the dimension sizes because there is a delimiter.
256  {
257  LVARRAY_ERROR_IF( lastChar=='{' || lastChar==',',
258  "character of ',' follows '"<<lastChar<<"'. Comma must follow an array value." );
259  if( dimSet[dimLevel]==false )
260  {
261  ++(dims[dimLevel]);
262  }
263  ++(currentDims[dimLevel]);
264  }
265  lastChar = c;
266  }
267  LVARRAY_ERROR_IF( dimLevel!=-1,
268  "Expression fails to close all '{' with a corresponding '}'. Check your input:"<<
269  valueString );
270 
271  array.resize( NDIM, dims );
272 
273 
274  // we need to replace our ',' with ' ' for reading in an array of strings, otherwise the
275  // stringstream::operator>> will grab the ','
276  std::replace( valueString.begin(), valueString.end(), ',', ' ' );
277 
278  // we also need to add a ' ' in front of any '}' otherwise the
279  // stringstream::operator>> will grab the }
280  for( std::string::size_type a=0; a<valueString.size(); ++a )
281  {
282  if( valueString[a] == '}' )
283  {
284  valueString.insert( a, " " );
285  ++a;
286  }
287  }
288  std::istringstream strstream( valueString );
289  // this recursively reads the values from the stringstream
290  internal::StringToArrayHelper< T, INDEX_TYPE >::Read( array.toSlice(), array.dims(), strstream );
291 }
292 
293 } // namespace input
294 } // namespace LvArray
void clear()
Sets the size of the Array to zero and destroys all the values.
Definition: Array.hpp:408
#define LVARRAY_ERROR_IF(EXP, MSG)
Abort execution if EXP is true.
Definition: Macros.hpp:101
This class serves to provide a sliced multidimensional interface to the family of LvArray classes...
Definition: ArraySlice.hpp:89
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:121
Contains the implementation of LvArray::Array.
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.
#define LVARRAY_ERROR(MSG)
Abort execution.
Definition: Macros.hpp:122
The top level namespace.
Definition: Array.hpp:24
void resize(int const numDims, DIMS_TYPE const *const dims)
Resize the dimensions of the Array to match the given dimensions.
Definition: Array.hpp:277
std::string string
String type.
Definition: DataTypes.hpp:131
This class provides a fixed dimensional resizeable array interface in addition to an interface simila...
Definition: Array.hpp:55