diff --git a/example/ticker.cpp b/example/ticker.cpp index c9d9195..0eb8e2c 100644 --- a/example/ticker.cpp +++ b/example/ticker.cpp @@ -8,9 +8,12 @@ */ #include +#include +#include + +#include #include -#include #include #include @@ -31,14 +34,16 @@ int main(int argc, char* argv[]) } if(argc>=3) { - double rate = 0.0; - - if(epicsParseDouble(argv[2], &rate, nullptr) || rate<=0.0) { - std::cerr<<"Rate must be a positive number, not "< #include #include -#include #include @@ -530,11 +529,15 @@ std::shared_ptr MonitorBuilder::exec() if(ackAny.type()==TypeCode::String) { auto sval = ackAny.as(); if(sval.size()>1 && sval.back()=='%') { - double percent=50.0; - char *units = nullptr; - if(epicsParseDouble(sval.c_str(), &percent, &units)==0 && units && units[0]=='%') { - if(percent>0.0 && percent<=100.0) + try { + auto percent = parseTo(sval); + if(percent>0.0 && percent<=100.0) { op->ackAt = uint32_t(percent * op->queueSize); + } else { + throw std::invalid_argument("not in range (0%, 100%]"); + } + }catch(std::exception&){ + log_warn_printf(monevt, "Error parsing as percent ackAny: \"%s\"\n", sval.c_str()); } } diff --git a/src/config.cpp b/src/config.cpp index 3aaa4bd..5c171cc 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -134,7 +134,7 @@ Config Config::from_env() if(const char *env = pickenv(&name, {"EPICS_PVAS_SERVER_PORT", "EPICS_PVA_SERVER_PORT"})) { try { - ret.tcp_port = lexical_cast(env); + ret.tcp_port = parseTo(env); }catch(std::exception& e) { log_err_printf(serversetup, "%s invalid integer : %s", name, e.what()); } @@ -142,7 +142,7 @@ Config Config::from_env() if(const char *env = pickenv(&name, {"EPICS_PVAS_BROADCAST_PORT", "EPICS_PVA_BROADCAST_PORT"})) { try { - ret.udp_port = lexical_cast(env); + ret.udp_port = parseTo(env); }catch(std::exception& e) { log_err_printf(serversetup, "%s invalid integer : %s", name, e.what()); } @@ -246,7 +246,7 @@ Config Config::from_env() if(const char *env = pickenv(&name, {"EPICS_PVA_BROADCAST_PORT"})) { try { - ret.udp_port = lexical_cast(env); + ret.udp_port = parseTo(env); }catch(std::exception& e) { log_err_printf(serversetup, "%s invalid integer : %s", name, e.what()); } diff --git a/src/data.cpp b/src/data.cpp index e1de3e4..bbf94e8 100644 --- a/src/data.cpp +++ b/src/data.cpp @@ -7,8 +7,6 @@ #include #include -#include - #include "dataimpl.h" #include "utilpvt.h" @@ -353,25 +351,16 @@ void Value::copyOut(void *ptr, StoreType type) const switch(type) { case StoreType::String: *reinterpret_cast(ptr) = src; return; case StoreType::Integer: { - epicsInt64 temp; - if(epicsParseInt64(src.c_str(), &temp, 0, nullptr)==0) { - *reinterpret_cast(ptr) = temp; - return; - } + *reinterpret_cast(ptr) = parseTo(src); + return; } case StoreType::UInteger: { - epicsUInt64 temp; - if(epicsParseUInt64(src.c_str(), &temp, 0, nullptr)==0) { - *reinterpret_cast(ptr) = temp; - return; - } + *reinterpret_cast(ptr) = parseTo(src); + return; } case StoreType::Real: { - double temp; - if(epicsParseDouble(src.c_str(), &temp, nullptr)==0) { - *reinterpret_cast(ptr) = temp; - return; - } + *reinterpret_cast(ptr) = parseTo(src); + return; } case StoreType::Bool: { if(src=="true") { *reinterpret_cast(ptr) = true; return; } @@ -432,31 +421,6 @@ bool Value::tryCopyOut(void *ptr, StoreType type) const namespace { -bool parseScalar(double& dest, const std::string& inp) -{ - return 0==epicsParseDouble(inp.c_str(), &dest, nullptr); -} - -bool parseScalar(int64_t& dest, const std::string& inp) -{ - epicsInt64 t; - if(epicsParseInt64(inp.c_str(), &t, 0, nullptr)==0) { - dest = t; - return true; - } - return false; -} - -bool parseScalar(uint64_t& dest, const std::string& inp) -{ - epicsUInt64 t; - if(epicsParseUInt64(inp.c_str(), &t, 0, nullptr)==0) { - dest = t; - return true; - } - return false; -} - // C-style cast between scalar storage types, and print to string (base 10) template bool copyInScalar(Dest& dest, const void *ptr, StoreType type) @@ -466,7 +430,7 @@ bool copyInScalar(Dest& dest, const void *ptr, StoreType type) case StoreType::Integer: dest = Dest(*reinterpret_cast(ptr)); return true; case StoreType::UInteger: dest = Dest(*reinterpret_cast(ptr)); return true; case StoreType::Bool: dest = Dest(*reinterpret_cast(ptr)); return true; - case StoreType::String: return parseScalar(dest, *reinterpret_cast(ptr)); + case StoreType::String: dest = parseTo(*reinterpret_cast(ptr)); return true; case StoreType::Null: case StoreType::Compound: case StoreType::Array: @@ -728,12 +692,11 @@ void Value::traverse(const std::string &expr, bool modify) maybedot = false; size_t sep = expr.find_first_of(']', pos); - unsigned long long index=0; if(expr[pos]=='[' - && sep!=std::string::npos && sep-pos>=2 - && !epicsParseULLong(expr.substr(pos+1, sep-1-pos).c_str(), &index, 0, nullptr)) + && sep!=std::string::npos && sep-pos>=2) { + auto index = parseTo(expr.substr(pos+1, sep-1-pos)); auto& varr = store->as>(); shared_array arr; if((varr.original_type()==ArrayType::Value) diff --git a/src/sharedarray.cpp b/src/sharedarray.cpp index cc9b65b..7bc693b 100644 --- a/src/sharedarray.cpp +++ b/src/sharedarray.cpp @@ -6,7 +6,6 @@ #include -#include #include #include @@ -164,37 +163,26 @@ void parseValue(bool& dest, const std::string& src) throw std::runtime_error(SB()<<"Expected \"true\" or \"false\", not \""< +typename std::enable_if::value && std::is_signed::value>::type +parseValue(Dest& dest, const std::string& src) +{ + dest = Dest(parseTo(src)); } -CASE(Int8, int8_t) -CASE(Int16, int16_t) -CASE(Int32, int32_t) -CASE(Int64, int64_t) -CASE(UInt8, uint8_t) -CASE(UInt16, uint16_t) -CASE(UInt32, uint32_t) -CASE(UInt64, uint64_t) - -#undef CASE - -#define CASE(ETYPE, TYPE) \ -void parseValue(TYPE& dest, const std::string& src) { \ - epics ## ETYPE temp; \ - if(epicsParse ## ETYPE(src.c_str(), &temp, nullptr)) \ - throw std::runtime_error(SB()<<"Expected " #TYPE ", not \""< +typename std::enable_if::value && !std::is_signed::value && !std::is_same::value>::type +parseValue(Dest& dest, const std::string& src) +{ + dest = Dest(parseTo(src)); } -CASE(Float32, float) -CASE(Float64, double) - -#undef CASE +template +typename std::enable_if::value>::type +parseValue(Dest& dest, const std::string& src) +{ + dest = Dest(parseTo(src)); +} template void convertFromStr(const void *sbase, void *dbase, size_t count) diff --git a/src/type.cpp b/src/type.cpp index 7534fef..00f19f5 100644 --- a/src/type.cpp +++ b/src/type.cpp @@ -7,8 +7,6 @@ #include #include -#include - #include "dataimpl.h" #include "utilpvt.h" diff --git a/src/util.cpp b/src/util.cpp index 4db2503..3784419 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -15,8 +15,6 @@ #include -#include - #include #include #include @@ -386,18 +384,35 @@ std::ostream& operator<<(std::ostream& strm, const SockAddr& addr) } // namespace pvxs namespace pvxs {namespace impl { -namespace idetail { template<> -unsigned short as_str::op(const char *s) -{ - epicsUInt16 ret; - if(int err = epicsParseUInt16(s, &ret, 0, nullptr)) { - (void)err; - throw std::runtime_error(SB()<<"Unable to parse as uint16 : "<(const std::string& s) { + size_t idx=0, L=s.size(); + double ret = std::stod(s, &idx); + for(; idx +uint64_t parseTo(const std::string& s) { + size_t idx=0, L=s.size(); + unsigned long long ret = std::stoull(s, &idx, 0); + for(; idx +int64_t parseTo(const std::string& s) { + size_t idx=0, L=s.size(); + long long ret = std::stoll(s, &idx, 0); + for(; idx -struct as_str {PVXS_API static T op(const char *s);}; -} // namespace idetail - -template -inline T lexical_cast(const char *s) -{ - return idetail::as_str::op(s); -} - -template -inline T lexical_cast(const std::string& s) -{ - return idetail::as_str::op(s.c_str()); -} - void indent(std::ostream& strm, unsigned level); namespace idetail { @@ -100,6 +82,19 @@ constexpr idetail::Range range(I end) { return idetail::Range{I(0), end}; template constexpr idetail::Range range(I begin, I end) { return idetail::Range{begin, end}; } +template +T parseTo(const std::string& s); // not implemented + +template<> +PVXS_API +double parseTo(const std::string& s); +template<> +PVXS_API +uint64_t parseTo(const std::string& s); +template<> +PVXS_API +int64_t parseTo(const std::string& s); + #ifdef _WIN32 # define RWLOCK_TYPE SRWLOCK # define RWLOCK_INIT(PLOCK) InitializeSRWLock(PLOCK) diff --git a/tools/call.cpp b/tools/call.cpp index 0fe905f..bc643f7 100644 --- a/tools/call.cpp +++ b/tools/call.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include @@ -27,12 +26,12 @@ namespace { void usage(const char* argv0) { std::cerr<<"Usage: "< = ...\n" - "\n" - " -h Show this message.\n" - " -V Print version and exit.\n" - " -v Make more noise.\n" - " -d Shorthand for $PVXS_LOG=\"pvxs.*=DEBUG\". Make a lot of noise.\n" - " -w Operation timeout in seconds. default 5 sec.\n" + "\n" + " -h Show this message.\n" + " -V Print version and exit.\n" + " -v Make more noise.\n" + " -d Shorthand for $PVXS_LOG=\"pvxs.*=DEBUG\". Make a lot of noise.\n" + " -w Operation timeout in seconds. default 5 sec.\n" ; } @@ -40,122 +39,124 @@ void usage(const char* argv0) int main(int argc, char *argv[]) { - logger_config_env(); // from $PVXS_LOG - double timeout = 5.0; - bool verbose = false; + try { + logger_config_env(); // from $PVXS_LOG + double timeout = 5.0; + bool verbose = false; - { - int opt; - while ((opt = getopt(argc, argv, "hVvdw:")) != -1) { - switch(opt) { - case 'h': - usage(argv[0]); - return 0; - case 'V': - std::cout<(optarg); + break; + default: + usage(argv[0]); + std::cerr<<"\nUnknown argument: "< keys; - std::map values; - - for(auto n : range(optind, argc)) { - std::string fv(argv[n]); - auto sep = fv.find_first_of('='); - - if(sep==std::string::npos) { - std::cerr<<"Error: expected = not \""< keys; + std::map values; - if(values.find(key)!=values.end()) { - std::cerr<<"Error: duplicate argument name "<= not \""< #include -#include #include #include @@ -45,104 +44,103 @@ void usage(const char* argv0) int main(int argc, char *argv[]) { - logger_config_env(); // from $PVXS_LOG - double timeout = 5.0; - bool verbose = false; - std::string request; - Value::Fmt::format_t format = Value::Fmt::Delta; - auto arrLimit = epicsUInt64(-1); + try { + logger_config_env(); // from $PVXS_LOG + double timeout = 5.0; + bool verbose = false; + std::string request; + Value::Fmt::format_t format = Value::Fmt::Delta; + auto arrLimit = uint64_t(-1); - { - int opt; - while ((opt = getopt(argc, argv, "hVvdw:r:#:F:")) != -1) { - switch(opt) { - case 'h': - usage(argv[0]); - return 0; - case 'V': - std::cout<(optarg); + break; + case 'r': + request = optarg; + break; + case '#': + arrLimit = parseTo(optarg); + break; + case 'F': + if(std::strcmp(optarg, "tree")==0) { + format = Value::Fmt::Tree; + } else if(std::strcmp(optarg, "delta")==0) { + format = Value::Fmt::Delta; + } else { + std::cerr<<"Warning: ignoring unknown format '"<> ops; - - std::atomic remaining{argc-optind}; - epicsEvent done; - - for(auto n : range(optind, argc)) { - - ops.push_back(ctxt.get(argv[n]) - .pvRequest(request) - .result([&argv, n, &remaining, &done, format, arrLimit](client::Result&& result) { - std::cout<> ops; + + std::atomic remaining{argc-optind}; + epicsEvent done; + + for(auto n : range(optind, argc)) { + + ops.push_back(ctxt.get(argv[n]) + .pvRequest(request) + .result([&argv, n, &remaining, &done, format, arrLimit](client::Result&& result) { + std::cout< #include -#include #include #include @@ -38,86 +37,88 @@ void usage(const char* argv0) int main(int argc, char *argv[]) { - logger_config_env(); // from $PVXS_LOG - double timeout = 5.0; - bool verbose = false; + try { + logger_config_env(); // from $PVXS_LOG + double timeout = 5.0; + bool verbose = false; - { - int opt; - while ((opt = getopt(argc, argv, "hVvdw:")) != -1) { - switch(opt) { - case 'h': - usage(argv[0]); - return 0; - case 'V': - std::cout<(optarg); + break; + default: + usage(argv[0]); + std::cerr<<"\nUnknown argument: "<> ops; - - std::atomic remaining{argc-optind}; - epicsEvent done; - - for(auto n : range(optind, argc)) { - - ops.push_back(ctxt.info(argv[n]) - .result([&argv, n, &remaining, &done](client::Result&& result) { - std::cout<> ops; + + std::atomic remaining{argc-optind}; + epicsEvent done; + + for(auto n : range(optind, argc)) { + + ops.push_back(ctxt.info(argv[n]) + .result([&argv, n, &remaining, &done](client::Result&& result) { + std::cout< #include -#include #include #include @@ -46,118 +45,120 @@ void usage(const char* argv0) int main(int argc, char *argv[]) { - logger_config_env(); // from $PVXS_LOG - bool verbose = false; - std::string request; - Value::Fmt::format_t format = Value::Fmt::Delta; - auto arrLimit = epicsUInt64(-1); + try { + logger_config_env(); // from $PVXS_LOG + bool verbose = false; + std::string request; + Value::Fmt::format_t format = Value::Fmt::Delta; + auto arrLimit = uint64_t(-1); - { - int opt; - while ((opt = getopt(argc, argv, "hVvdr:#:F:")) != -1) { - switch(opt) { - case 'h': - usage(argv[0]); - return 0; - case 'V': - std::cout<(optarg); + break; + case 'F': + if(std::strcmp(optarg, "tree")==0) { + format = Value::Fmt::Tree; + } else if(std::strcmp(optarg, "delta")==0) { + format = Value::Fmt::Delta; + } else { + std::cerr<<"Warning: ignoring unknown format '"<> ops; - - std::atomic remaining{argc-optind}; - epicsEvent done; - - for(auto n : range(optind, argc)) { - - ops.push_back(ctxt.monitor(argv[n]) - .pvRequest(request) - .event([&argv, n, verbose, &remaining, &done, format, arrLimit](client::Subscription& mon) - { - - try { - while(auto update = mon.pop()) { - log_info_printf(app, "%s POP data\n", argv[n]); - std::cout<> ops; + + std::atomic remaining{argc-optind}; + epicsEvent done; + + for(auto n : range(optind, argc)) { + + ops.push_back(ctxt.monitor(argv[n]) + .pvRequest(request) + .event([&argv, n, verbose, &remaining, &done, format, arrLimit](client::Subscription& mon) + { + + try { + while(auto update = mon.pop()) { + log_info_printf(app, "%s POP data\n", argv[n]); + std::cout< #include -#include #include #include @@ -39,119 +38,121 @@ void usage(const char* argv0) int main(int argc, char *argv[]) { - logger_config_env(); // from $PVXS_LOG - double timeout = 5.0; - bool verbose = false; - std::string request; + try { + logger_config_env(); // from $PVXS_LOG + double timeout = 5.0; + bool verbose = false; + std::string request; - { - int opt; - while ((opt = getopt(argc, argv, "hvVdw:r:")) != -1) { - switch(opt) { - case 'h': - usage(argv[0]); - return 0; - case 'V': - std::cout<(optarg); + break; + case 'r': + request = optarg; + break; + default: + usage(argv[0]); + std::cerr<<"\nUnknown argument: "< values; - - if(argc-optind==1 && std::string(argv[optind]).find_first_of('=')==std::string::npos) { - // only one field assignment, and field name omitted. - // implies .value - - values["value"] = argv[optind]; - - } else { - for(auto n : range(optind, argc)) { - std::string fv(argv[n]); - auto sep = fv.find_first_of('='); - - if(sep==std::string::npos) { - std::cerr<<"Error: expected = not \""< values; + + if(argc-optind==1 && std::string(argv[optind]).find_first_of('=')==std::string::npos) { + // only one field assignment, and field name omitted. + // implies .value + + values["value"] = argv[optind]; + + } else { + for(auto n : range(optind, argc)) { + std::string fv(argv[n]); + auto sep = fv.find_first_of('='); + + if(sep==std::string::npos) { + std::cerr<<"Error: expected = not \""< Value { - auto val = std::move(prototype); - for(auto& pair : values) { - try{ - val[pair.first] = pair.second; - }catch(NoConvert& e){ - throw std::runtime_error(SB()<<"Unable to assign "< Value { + auto val = std::move(prototype); + for(auto& pair : values) { + try{ + val[pair.first] = pair.second; + }catch(NoConvert& e){ + throw std::runtime_error(SB()<<"Unable to assign "<