1 /*! 2 * \file mfront/src/CppMaterialPropertyInterface.cxx 3 * \brief 4 * \author Thomas Helfer 5 * \date 06 mai 2008 6 * \copyright Copyright (C) 2006-2018 CEA/DEN, EDF R&D. All rights 7 * reserved. 8 * This project is publicly released under either the GNU GPL Licence 9 * or the CECILL-A licence. A copy of thoses licences are delivered 10 * with the sources of TFEL. CEA or EDF may also distribute this 11 * project under specific licensing conditions. 12 */ 13 14 #include<sstream> 15 #include<fstream> 16 #include<algorithm> 17 #include<stdexcept> 18 19 #include"TFEL/Raise.hxx" 20 #include"TFEL/Config/GetInstallPath.hxx" 21 #include"TFEL/System/System.hxx" 22 23 #include"MFront/DSLUtilities.hxx" 24 #include"MFront/MFrontUtilities.hxx" 25 #include"MFront/MFrontHeader.hxx" 26 #include"MFront/FileDescription.hxx" 27 #include"MFront/TargetsDescription.hxx" 28 #include"MFront/MaterialPropertyDescription.hxx" 29 #include"MFront/CppMaterialPropertyInterface.hxx" 30 31 namespace mfront 32 { 33 getHeaderFileName(const std::string & n)34 static std::string getHeaderFileName(const std::string& n){ 35 return "include/"+n+"-cxx.hxx"; 36 } 37 getName()38 std::string CppMaterialPropertyInterface::getName() 39 { 40 return "c++"; 41 } 42 43 CppMaterialPropertyInterface::CppMaterialPropertyInterface() = default; 44 45 std::pair<bool,tfel::utilities::CxxTokenizer::TokensContainer::const_iterator> treatKeyword(const std::string & k,const std::vector<std::string> & i,tokens_iterator current,const tokens_iterator)46 CppMaterialPropertyInterface::treatKeyword(const std::string& k, 47 const std::vector<std::string>& i, 48 tokens_iterator current, 49 const tokens_iterator) 50 { 51 tfel::raise_if((std::find(i.begin(),i.end(),"c++")!=i.end())|| 52 (std::find(i.begin(),i.end(),"C++")!=i.end())|| 53 (std::find(i.begin(),i.end(),"cxx")!=i.end())|| 54 (std::find(i.begin(),i.end(),"Cxx")!=i.end())|| 55 (std::find(i.begin(),i.end(),"cpp")!=i.end())|| 56 (std::find(i.begin(),i.end(),"Cpp")!=i.end()), 57 "CppMaterialPropertyInterface::treatKeyword: " 58 "unsupported keyword '"+k+"'"); 59 return {false,current}; 60 } // end of treatKeyword 61 62 void getTargetsDescription(TargetsDescription & d,const MaterialPropertyDescription & mpd) const63 CppMaterialPropertyInterface::getTargetsDescription(TargetsDescription& d, 64 const MaterialPropertyDescription& mpd) const 65 { 66 const auto lib = "Cpp"+getMaterialLawLibraryNameBase(mpd); 67 const auto name = mpd.material.empty() ? mpd.className : mpd.material+"_"+mpd.className; 68 const auto tfel_config = tfel::getTFELConfigExecutableName(); 69 insert_if(d[lib].cppflags, 70 "$(shell "+tfel_config+" --cppflags --compiler-flags)"); 71 insert_if(d[lib].include_directories, 72 "$(shell "+tfel_config+" --include-path)"); 73 insert_if(d[lib].sources,name+"-cxx.cxx"); 74 insert_if(d.headers,getHeaderFileName(name)); 75 #if !((defined _WIN32) && (defined _MSC_VER)) 76 insert_if(d[lib].link_libraries,"m"); 77 #endif /* !((defined _WIN32) && (defined _MSC_VER)) */ 78 auto cn = std::string{}; 79 #pragma message("handle namespace") 80 // for(const auto& ns : this->namespaces){ 81 // cc += ns + "::" 82 // } 83 cn += name; 84 insert_if(d[lib].epts,cn); 85 } // end of CMaterialPropertyInterface::getTargetsDescription 86 writeOutputFiles(const MaterialPropertyDescription & mpd,const FileDescription & fd) const87 void CppMaterialPropertyInterface::writeOutputFiles(const MaterialPropertyDescription& mpd, 88 const FileDescription& fd) const 89 { 90 this->writeHeaderFile(mpd,fd); 91 this->writeSrcFile(mpd,fd); 92 } // end of CppMaterialPropertyInterface::writeOutputFiles 93 writeHeaderFile(const MaterialPropertyDescription & mpd,const FileDescription & fd) const94 void CppMaterialPropertyInterface::writeHeaderFile(const MaterialPropertyDescription& mpd, 95 const FileDescription& fd) const 96 { 97 const auto name = mpd.material.empty() ? mpd.className : mpd.material+"_"+mpd.className; 98 std::ofstream header(getHeaderFileName(name)); 99 tfel::raise_if(!header,"CppMaterialPropertyInterface::writeHeaderFile: " 100 "unable to open '"+getHeaderFileName(name)+"' " 101 "for writing output file."); 102 header.exceptions(std::ios::badbit|std::ios::failbit); 103 header << "/*!\n" 104 << "* \\file " << getHeaderFileName(name) << '\n' 105 << "* \\brief " << "this file declares the " 106 << name << " MaterialLaw.\n" 107 << "* File generated by " 108 << MFrontHeader::getVersionName() << " " 109 << "version " << MFrontHeader::getVersionNumber() << '\n'; 110 if(!fd.authorName.empty()){ 111 header << "* \\author " << fd.authorName << '\n'; 112 } 113 if(!fd.date.empty()){ 114 header << "* \\date " << fd.date << '\n'; 115 } 116 if(!fd.description.empty()){ 117 header << fd.description << '\n'; 118 } 119 header << " */\n\n"; 120 121 header << "#ifndef LIB_MFRONT_" << makeUpperCase(name) << "_HXX\n" 122 << "#define LIB_MFRONT_" << makeUpperCase(name) << "_HXX\n\n" 123 << "#include<ostream>\n" 124 << "#include<cmath>\n" 125 << "#include<algorithm>\n" 126 << "#include<stdexcept>\n\n" 127 << "#include<functional>\n\n"; 128 if(!mpd.includes.empty()){ 129 header << mpd.includes << '\n'; 130 } 131 writeExportDirectives(header); 132 header << "namespace mfront\n{\n\n" 133 << "struct MFRONT_SHAREDOBJ " << name << '\n'; 134 if(!mpd.inputs.empty()){ 135 if(mpd.inputs.size()==1){ 136 header << ": std::unary_function<double,double>\n"; 137 } else if(mpd.inputs.size()==2){ 138 header << ": std::binary_function<double,double,double>\n"; 139 } 140 } 141 header << "{\n\n"; 142 if(mpd.inputs.empty()){ 143 header << "//! nested typedef to make " << name 144 << " model an adaptable generator (STL compliance)\n\n"; 145 header << "typedef double result_type;\n\n"; 146 } 147 header << "//! default constructor\n" 148 << name << "() noexcept;\n\n" 149 << "//! move constructor\n" 150 << name << "(" << name << "&&) noexcept = default;\n" 151 << "//! copy constructor\n" 152 << name << "(const " << name << "&) noexcept = default;\n" 153 << "//! move assignement operator\n" 154 << name << "& operator=(" << name << "&&) noexcept = default;\n" 155 << "//! assignement operator\n" 156 << name << "& operator=(const " << name << "&) noexcept = default;\n\n" 157 << "double\noperator()("; 158 for(auto p4=mpd.inputs.begin();p4!=mpd.inputs.end();){ 159 header << "const double"; 160 if((++p4)!=mpd.inputs.end()){ 161 header << ","; 162 } 163 } 164 header << ") const;\n\n"; 165 if((hasBounds(mpd.inputs))||(hasPhysicalBounds(mpd.inputs))){ 166 header << "static void\ncheckBounds("; 167 for(auto p4=mpd.inputs.begin();p4!=mpd.inputs.end();){ 168 header << "const double"; 169 if((++p4)!=mpd.inputs.end()){ 170 header << ","; 171 } 172 } 173 header << ");\n\n"; 174 } 175 for(const auto& p : mpd.parameters){ 176 header << "const double& get" << p.name << "() const;\n"; 177 } 178 for(const auto& p : mpd.parameters){ 179 header << "double& get" << p.name << "();\n"; 180 } 181 for(const auto& p : mpd.parameters){ 182 header << "void set" << p.name << "(const double);\n"; 183 } 184 if(!mpd.parameters.empty()){ 185 header << "private:\n"; 186 for(const auto& p : mpd.parameters){ 187 header << "double " << p.name << ";\n"; 188 } 189 } 190 header << "}; // end of class " << name << "\n\n" 191 << "std::ostream& operator<<(std::ostream&,const " << name << "&);\n\n" 192 << "} // end of namespace mfront\n\n" 193 << "#endif /* LIB_MFRONT_" << makeUpperCase(name) << "_HXX */\n"; 194 header.close(); 195 } // end of CppMaterialPropertyInterface::writeHeaderFile() 196 writeSrcFile(const MaterialPropertyDescription & mpd,const FileDescription & fd) const197 void CppMaterialPropertyInterface::writeSrcFile(const MaterialPropertyDescription& mpd, 198 const FileDescription& fd) const 199 { 200 auto throw_if = [](const bool b,const std::string& m){ 201 tfel::raise_if(b,"CppMaterialPropertyInterface::writeSrcFile: "+m); 202 }; 203 const auto name = mpd.material.empty() ? 204 mpd.className : mpd.material+"_"+mpd.className; 205 const auto src_name = "src/" +name+"-cxx.cxx"; 206 std::ofstream src(src_name); 207 throw_if(!src,"unable to open '"+src_name+"'"); 208 src.exceptions(std::ios::badbit|std::ios::failbit); 209 src << "/*!\n" 210 << "* \\file " << src_name << '\n' 211 << "* \\brief " << "this file implements the " 212 << name << " MaterialLaw.\n" 213 << "* File generated by " 214 << MFrontHeader::getVersionName() << " " 215 << "version " << MFrontHeader::getVersionNumber() 216 << '\n'; 217 if(!fd.authorName.empty()){ 218 src << "* \\author " << fd.authorName << '\n'; 219 } 220 if(!fd.date.empty()){ 221 src << "* \\date " << fd.date << '\n'; 222 } 223 src << " */\n\n" 224 << "#include<stdexcept>\n" 225 << "#include<iostream>\n" 226 << "#include<cstdlib>\n" 227 << "#include<sstream>\n" 228 << "#include<cstring>\n" 229 << "#include<vector>\n" 230 << "#include<string>\n" 231 << "#include\"TFEL/Raise.hxx\"\n" 232 << "#include\"TFEL/Math/General/IEEE754.hxx\"\n" 233 << "#include\"" << name << "-cxx.hxx\"\n\n"; 234 writeExportDirectives(src); 235 src << "#ifdef __cplusplus\n" 236 << "extern \"C\"{\n" 237 << "#endif /* __cplusplus */\n\n"; 238 // mfront metadata 239 writeEntryPointSymbol(src,name); 240 writeTFELVersionSymbol(src,name); 241 writeInterfaceSymbol(src,name,"C++"); 242 writeMaterialSymbol(src,name,mpd.material); 243 writeMaterialKnowledgeTypeSymbol(src,name,MATERIALPROPERTY); 244 src << "#ifdef __cplusplus\n" 245 << "} // end of extern \"C\"\n" 246 << "#endif /* __cplusplus */\n\n"; 247 src << "namespace mfront\n{\n\n" 248 << name << "::" << name << "() noexcept\n"; 249 if(!mpd.parameters.empty()){ 250 src << ": "; 251 } 252 for(auto p=mpd.parameters.begin();p!=mpd.parameters.end();){ 253 throw_if(!p->hasAttribute(VariableDescription::defaultValue), 254 "internal error (can't find value of " 255 "parameter '" + p->name + "')"); 256 src << p->name << "(" 257 << p->getAttribute<double>(VariableDescription::defaultValue) << ")"; 258 if(++p!=mpd.parameters.end()){ 259 src << ","; 260 } 261 src << "\n"; 262 } 263 src << "{} // end of " << name << "::" << name << "\n\n"; 264 for(const auto& p : mpd.parameters){ 265 src << "const double& "; 266 src << name; 267 src << "::get" 268 << p.name << "() const{\n" 269 << "return this->" << p.name << ";\n" 270 << "} // end of " << name << "::get\n\n"; 271 src << "double& " << name << "::get" << p.name << "(){\n" 272 << "return " << p.name << ";\n" 273 << "} // end of " << name << "::get\n\n"; 274 src << "void " << name; 275 src << "::set" << p.name; 276 src << "(const double " << name << "_value_)"; 277 src << "{\n" 278 << "this->" << p.name << " = " << name << "_value_;\n" 279 << "} // end of " << name << "::set\n\n"; 280 } 281 src << "double " << name << "::operator()("; 282 for(auto pi=mpd.inputs.begin();pi!=mpd.inputs.end();){ 283 src << "const double " << pi->name; 284 if(++pi!=mpd.inputs.end()){ 285 src << ","; 286 } 287 } 288 src << ") const\n{\n" 289 << "using namespace std;\n" 290 << "using real = double;\n"; 291 // material laws 292 writeMaterialLaws(src,mpd.materialLaws); 293 // static variables 294 writeStaticVariables(src,mpd.staticVars,fd.fileName); 295 // body 296 src << "real " << mpd.output.name << ";\n"; 297 if((hasBounds(mpd.inputs))||(hasPhysicalBounds(mpd.inputs))){ 298 src << "#ifndef MFRONT_NO_BOUNDS_CHECK\n"; 299 src << name << "::checkBounds("; 300 for(auto pi=mpd.inputs.begin();pi!=mpd.inputs.end();){ 301 src << pi->name; 302 if((++pi)!=mpd.inputs.end()){ 303 src << ","; 304 } 305 } 306 src << ");\n"; 307 src << "#endif /* MFRONT_NO_BOUNDS_CHECK */\n"; 308 } 309 if(!mpd.inputs.empty()){ 310 src << "#ifndef MFRONT_NOERRNO_HANDLING\n" 311 << "const auto mfront_errno_old = errno;\n" 312 << "errno=0;\n" 313 << "#endif /* MFRONT_NOERRNO_HANDLING */\n"; 314 } 315 src << mpd.f.body; 316 if(!mpd.inputs.empty()){ 317 src << "#ifndef MFRONT_NOERRNO_HANDLING\n" 318 // can't use std::swap here as errno might be a macro 319 << "const auto mfront_errno = errno;\n" 320 << "errno = mfront_errno_old;\n" 321 << "tfel::raise_if((mfront_errno!=0)||" 322 << "(!tfel::math::ieee754::isfinite(" << mpd.output.name << ")),\n" 323 << "\""<< name << ": errno has been set \"\n" 324 << "\"(\"+std::string(::strerror(errno))+\")\");\n" 325 << "#endif /* MFRONT_NOERRNO_HANDLING */\n"; 326 } 327 src << "return " << mpd.output.name << ";\n" 328 << "} // end of " << name << "::operator()\n\n"; 329 if((hasBounds(mpd.inputs))||(hasPhysicalBounds(mpd.inputs))){ 330 src << "void\n"; 331 src << name; 332 src << "::checkBounds("; 333 if(!mpd.inputs.empty()){ 334 for(auto pi=mpd.inputs.begin();pi!=mpd.inputs.end();){ 335 src << "const double " << pi->name; 336 if((++pi)!=mpd.inputs.end()){ 337 src << ","; 338 } 339 } 340 } else { 341 src << "void"; 342 } 343 src << ")\n{\n"; 344 if(hasPhysicalBounds(mpd.inputs)){ 345 src << "using namespace std;\n" 346 << "// treating physical bounds\n"; 347 for(const auto& i : mpd.inputs){ 348 if(!i.hasPhysicalBounds()){ 349 continue; 350 } 351 const auto& b = i.getPhysicalBounds(); 352 if(b.boundsType==VariableBoundsDescription::LOWER){ 353 src << "if(" << i.name<< " < "<< b.lowerBound << "){\n" 354 << "ostringstream msg;\n" 355 << "msg << \"" << name << " : " << i.name << " is below its physical lower bound \";\n" 356 << "msg << \"(\" << " << i.name 357 << " << \" < " << b.lowerBound << ")\";\n" 358 << "tfel::raise<range_error>(msg.str());\n" 359 << "}\n"; 360 } else if(b.boundsType==VariableBoundsDescription::UPPER){ 361 src << "if(" << i.name<< " > "<< b.upperBound << "){\n" 362 << "ostringstream msg;\n" 363 << "msg << \"" << name << " : " << i.name << " is beyond its physical upper bound \";\n" 364 << "msg << \"(\" << " << i.name 365 << " << \" > " << b.upperBound << ")\";\n" 366 << "tfel::raise<range_error>(msg.str());\n" 367 << "}\n"; 368 } else { 369 src << "if(" << i.name<< " < "<< b.lowerBound << "){\n" 370 << "ostringstream msg;\n" 371 << "msg << \"" << name << " : " << i.name << " is below its physical lower bound \";\n" 372 << "msg << \"(\" << " << i.name 373 << " << \" < " << b.lowerBound << ")\";\n" 374 << "tfel::raise<range_error>(msg.str());\n" 375 << "}\n" 376 << "if(" << i.name<< " > "<< b.upperBound << "){\n" 377 << "ostringstream msg;\n" 378 << "msg << \"" << name << " : " << i.name << " is beyond its physical upper bound \";\n" 379 << "msg << \"(\" << " << i.name 380 << " << \" > " << b.upperBound << ")\";\n" 381 << "tfel::raise<range_error>(msg.str());\n" 382 << "}\n"; 383 } 384 } 385 } 386 if(hasBounds(mpd.inputs)){ 387 src << "// treating standard bounds\n"; 388 for(const auto& i : mpd.inputs){ 389 if(!i.hasBounds()){ 390 continue; 391 } 392 const auto& b = i.getBounds(); 393 if(b.boundsType==VariableBoundsDescription::LOWER){ 394 src << "if(" << i.name<< " < "<< b.lowerBound << "){\n" 395 << "const char * const policy = " 396 << "::getenv(\"OUT_OF_BOUNDS_POLICY\");\n" 397 << "if(policy!=nullptr){\n" 398 << "if(strcmp(policy,\"STRICT\")==0){\n" 399 << "ostringstream msg;\n" 400 << "msg << \"" << name << " : " << i.name << " is below its lower bound \";\n" 401 << "msg << \"(\" << " << i.name 402 << " << \" < " << b.lowerBound << ")\";\n" 403 << "tfel::raise<range_error>(msg.str());\n" 404 << "} else if(strcmp(policy,\"WARNING\")==0){\n" 405 << "cerr << \"" << i.name << " is below its lower bound \";\n" 406 << "cerr << \"(\" << " << i.name 407 << " << \" < " << b.lowerBound << ")\\n\";\n" 408 << "}\n" 409 << "}\n" 410 << "}\n"; 411 } else if(b.boundsType==VariableBoundsDescription::UPPER){ 412 src << "if(" << i.name<< " > "<< b.upperBound << "){\n" 413 << "const char * const policy = " 414 << "::getenv(\"OUT_OF_BOUNDS_POLICY\");\n" 415 << "if(policy!=nullptr){\n" 416 << "if(strcmp(policy,\"STRICT\")==0){\n" 417 << "ostringstream msg;\n" 418 << "msg << \"" << name << " : " << i.name << " is beyond its upper bound \";\n" 419 << "msg << \"(\" << " << i.name 420 << " << \" > " << b.upperBound << ")\";\n" 421 << "tfel::raise<range_error>(msg.str());\n" 422 << "} else if(strcmp(policy,\"WARNING\")==0){\n" 423 << "cerr << \"" << i.name << " is beyond its upper bound \";\n" 424 << "cerr << \"(\" << " << i.name 425 << " << \" > " << b.upperBound << ")\\n\";\n" 426 << "}\n" 427 << "}\n" 428 << "}\n"; 429 } else { 430 src << "if(" << i.name<< " < "<< b.lowerBound << "){\n" 431 << "const char * const policy = " 432 << "::getenv(\"OUT_OF_BOUNDS_POLICY\");\n" 433 << "if(policy!=nullptr){\n" 434 << "if(strcmp(policy,\"STRICT\")==0){\n" 435 << "ostringstream msg;\n" 436 << "msg << \"" << name << " : " << i.name << " is below its lower bound \";\n" 437 << "msg << \"(\" << " << i.name 438 << " << \" < " << b.lowerBound << ")\";\n" 439 << "tfel::raise<range_error>(msg.str());\n" 440 << "} else if(strcmp(policy,\"WARNING\")==0){\n" 441 << "cerr << \"" << i.name << " is below its lower bound \";\n" 442 << "cerr << \"(\" << " << i.name 443 << " << \" < " << b.lowerBound << ")\\n\";\n" 444 << "}\n" 445 << "}\n" 446 << "}\n" 447 << "if(" << i.name<< " > "<< b.upperBound << "){\n" 448 << "const char * const policy = " 449 << "::getenv(\"OUT_OF_BOUNDS_POLICY\");\n" 450 << "if(policy!=nullptr){\n" 451 << "if(strcmp(policy,\"STRICT\")==0){\n" 452 << "ostringstream msg;\n" 453 << "msg << \"" << name << " : " << i.name << " is beyond its upper bound \";\n" 454 << "msg << \"(\" << " << i.name 455 << " << \" > " << b.upperBound << ")\";\n" 456 << "tfel::raise<range_error>(msg.str());\n" 457 << "} else if(strcmp(policy,\"WARNING\")==0){\n" 458 << "cerr << \"" << i.name << " is beyond its upper bound \";\n" 459 << "cerr << \"(\" << " << i.name 460 << " << \" > " << b.upperBound << ")\\n\";\n" 461 << "}\n" 462 << "}\n" 463 << "}\n"; 464 } 465 } 466 } 467 src << "} // end of " << name << "::checkBounds\n\n"; 468 } 469 src << "std::ostream& operator<<(std::ostream& os,const " << name; 470 if(!mpd.parameters.empty()){ 471 src << "& src){\n"; 472 } else { 473 src << "&){\n"; 474 } 475 for(const auto& p : mpd.parameters){ 476 src << "os << \"" << p.name 477 << " : \" << src.get" << p.name << "() << std::endl;\n"; 478 } 479 src << "return os;\n}// end of operator(std::ostream& os," 480 << name << "\n\n" 481 << "} // end of namespace mfront\n\n"; 482 src.close(); 483 } // end of CppMaterialPropertyInterface::writeSrcFile() 484 485 CppMaterialPropertyInterface::~CppMaterialPropertyInterface() = default; 486 487 } // end of namespace mfront 488