c++ - Saving class instances with different template parameters inside one vector but keep their properties -
i have program parses , manages command-line parameters me. can see in main-function, using simple commands option<int>("number", { "-n", "--number" }) can specify type option's value should have (like int in case), unique identifier each option (like "number"), , multiple strings option can introduced with. also, many options should wrapped in class called optionset, simplifies access options.
but in actual code, having several problems right now:
- i want store multiple instances of 1 class different template parameters within 1
std::vector. example, in code,option<int>should stored in same vectoroption<std::string>,option<double>.
maybe it's possible store template parameters separately in vector? - by using
using,std::enable_if_t,std::is_samecreated type calledoptionhasvalue. if template parameterinvertfalse ,tvoid,optionhasvaluehas invalid type, otherwise has type specified template parameteru.
classoptionvalueusesoptionhasvalue, bit of sfinae magic decide if should have needed methods supporting storage of values or not. is, first version ofoptionvaluehasoptionhasvalue<t>second template parameter, becomes invalid (and removed compiler) iftvoid. other version ofoptionvaluehas opposite behavior, because second template parameteroptionhasvalue<t, true>,trueinverts behavior ofoptionhasvalue.
classoptioninheritsoptionvalue, if create optionoption<void>, not have support values (that is, lacks functionssetvalue,setvaluefromstring,getvalueshould). on other hand, if create optionoption<int>, resulting class instance has of these features.
problem is, (for example)optionset::process()accesses bothoption::hasvalue,option::setvaluefromstring, latter declared ifoption::hasvaluetrue (and corresponding template parameter option notvoid). becauseoption::setvaluefromstringnot wrapped in kind of template here, compiler complains. - in
main-function use functionoptionset.getoptionvalue(std::string). function should return value of option (after has been set afterprocess()has been called). difficult thing return type depends on return value offindoptionbyidentifier, function loops through available options , returns option wanted identifier.
example, ifidentifier"number"(as in exampleoption@ beginning of question), return type offindoptionbyidentifieroption<int>, because option having identifier"number"1 hasintfirst template parameter, result ingetoptionvaluehaving return typeint.
can see expected behavior in comments in of last lines ofmain-function.
so, have change in following code fix these things (and make compile)? using g++ 5.2.0 (mingw-w64), may use feature of c++11 , c++14.
#include <iostream> #include <vector> #include <string> #include <algorithm> #include <stdexcept> #include <type_traits> #include <boost/lexical_cast.hpp> #include <boost/any.hpp> template<typename t, bool invert = false, typename u = void> using optionhasvalue = std::enable_if_t<(!std::is_same<t, void>::value) ^ invert, u>; //only make template substitution successful, if (when 'invert' false) t not if type 'void' template<typename t, typename enable = void> class optionvalue; template<typename t> class optionvalue<t, optionhasvalue<t>> //using sfinae ("substitution failure not error") here { protected: t value; public: void setvalue(t newvalue) { value = newvalue; } void setvaluefromstring(std::string newvaluestr) { setvalue(boost::lexical_cast<t>(newvaluestr)); } t getvalue() { return value; } bool hasvalue() { return true; //if class variant taken compiler, 'option' inherit have value } }; template<typename t> class optionvalue<t, optionhasvalue<t, true>> //the opposite condition (the 'true' inverts it) { //option value disabled, check if value available in derived class, add function (or should not?) public: bool hasvalue() { return false; } }; template<typename t> class option : public optionvalue<t> { private: std::string identifier; std::vector<std::string> variants; public: option(std::string newidentifier, std::vector<std::string> newvariants) { identifier = newidentifier; variants = newvariants; } bool hasvariant(std::string v) { return (std::find(variants.begin(), variants.end(), v) != variants.end()); } std::string getidentifier() { return identifier; } }; class optionset { private: std::vector<boost::any> options; //boost::any can't right way this, or it? std::vector<std::string> argvvec; template<typename t> option<t>& findoptionbyidentifier(std::string identifier) { for(auto& o : options) if(o.getidentifier() == identifier) //of course doesn't compile, because 'o' of type 'boost::any', should instead? return o; throw std::runtime_error("error: unable find option identifier \"" + identifier + "\"\n"); } template<typename t> option<t>& findoptionbyvariant(std::string variant) { for(auto& o : options) if(o.hasvariant(variant)) //probably same compile error in 'findoptionbyidentifier' return o; throw std::runtime_error("error: unable find option variant \"" + variant + "\"\n"); } public: template<typename t> void add(option<t> opt) { options.push_back(opt); //is right way add instances of classes different template parameters vector? } void setargvvec(std::vector<std::string> newargvvec) { argvvec = newargvvec; } void process() { for(size_t i=0; i<argvvec.size(); i++) { option<t>& opt = findoptionbyvariant(argvvec[i]); //of course doesn't compile either, should instead? if(opt.hasvalue()) { if(i == argvvec.size()-1) throw std::runtime_error("error: no value given option \"" + argvvec[i] + "\"\n"); opt.setvaluefromstring(argvvec[i]); //boost::bad_lexical_cast should caught here, that's not important right i++; } } } template<typename t> t getoptionvalue(std::string identifier) { option<t>& opt = findoptionbyidentifier(identifier); //a bit call 'findoptionbyvariant' in 'process()'. also, variable not have reference if(!opt.hasvalue()) throw std::runtime_error("error: option identifier \"" + identifier + "\" has no value\n"); return opt.getvalue(); } }; int main() { optionset optionset; //it's not guaranteed optionset::add receive rvalue, here shorter code/simplicity optionset.add(option<void>("help", { "-?", "--help" })); //if it's void-option, 'option' not have value, if template parameter else, has 1 (like below) optionset.add(option<std::string>("message", { "-m", "--message" })); optionset.add(option<int>("number", { "-n", "--number" })); optionset.add(option<double>("pi", { "-p", "--pi" })); optionset.setargvvec({ "--help", "-m", "hello", "--number", "100", "--pi", "3.14" }); optionset.process(); std::string message = optionset.getoptionvalue("message"); int number = optionset.getoptionvalue("number"); double pi = optionset.getoptionvalue("pi"); std::cout << "message: " << message << "\n"; //should output 'hello' std::cout << "number: " << number << "\n"; //should output '100' std::cout << "pi: " << pi << "\n"; //should output '3.140000' return 0; }
i not sure understood question, try answer it.
i want store multiple instances of 1 class different template parameters
there no such thing. template different template paramter different class. however, seem solving through boost::any. use type-erasure technique - example, have non-template parent options, or switch non-type-erasure boost::variant, seems have limited number of possible option types.
by using using, std::enable_if_t , std::is_same created type called optionhasvalue...
first of all, not use sfinae in example. simple partial specialization suffice. opt.setvaluefromstring(argvvec[i]); create noop function in void option class.
as last question, use templated function receives reference return type, instead of returning it.
Comments
Post a Comment