0

I've a class withmap<string,vector<string>>. I want to give a user a member function to receive the value for a key in different formats than std::vector(string):

vector(string), string, vector(int), vector(float) and bool

example:

 bool x =  (bool) myClass["dummy_boolean_type"]
 x = myClass["dummy_boolean_type"].as<bool>
 int y = (int) myClass["dummy_boolean_type"]

Can someone have an example what is the best way to achieve it ? (without a use in Boost)

8
  • myClass["dummy_boolean_type"]<bool> that's not valid... Commented Jul 6, 2015 at 15:30
  • 1
    What would returning string or bool mean for the user? Like, when would the bool be true or false? Commented Jul 6, 2015 at 15:30
  • So basically you want to store different types in your map? Commented Jul 6, 2015 at 15:30
  • 1
    possible duplicate of How to convert a number to string and vice versa in C++ Commented Jul 6, 2015 at 15:32
  • However it really unclear for few-times reading, but I've got the idea: author wants std::vector to be convertible to types: bool, std::vector<int>, std::vector<float>. Am I right, @Shaul ? Commented Jul 6, 2015 at 15:42

1 Answer 1

1

You cannot provide your class with any member function or functions that will support the two constructions you want:

T x = myClassInstance["key"].as<T>  // (1)
T x =  (T) myClassInstance["key"]   // (2)

In the case of (1), the immediate reason is simply that the construction is not legal C++. So let's suppose it is replaced by:

T x = myClassInstance["key"].as<T>()  // (1a)

But this doesn't help, and the reason is the same for both (1a) and (2):

The expression myClassInstance["key"] will have to evaluate to some object e or reference to such that is returned by:

myClass::operator[](std::string const &);

This e is the vector<string> to which key maps in your map<string,vector<string> data member of myClass. It is not myClassInstance.

So what you are asking for are member functions of myClass that will support the constructions:

T x = e.as<T> // (1b)
T x =  (T) e  // (2b)

where e is an std::vector<std::string>.

Clearly, nothing you can do in myClass can address your requirement. In (1b) and (2b), myClass is nowhere to be seen.

Is there any template member function of std::vector<std::string>:

template<typename T>
T as() const;

that has the behaviour you want for (1b)?

Does std::vector<std::string> have any template member function:

template<typename T>
operator T() const;

that has the behaviour you want for (2b)?

No and No.

However...

You can implement an as template member function of myClass that supports conversions such as you mention. Its signature - of course - would be:

template<typename T>
T myClass::as(std::string const & key) const;

and you would invoke it like:

T x = myClassInstance.as<T>("key")  // (1c)

I'll sketch an implemention that assumes we're content with the conversions from std::vector<std::string> to:

  • std::string
  • std::vector<int>
  • bool

which will be enough to get you under way.

To convert an std::vector<std::string> vs to std::string I'll concatenate the elements of vs.

To convert an std::vector<std::string> vs to std::vector<int> I'll convert each element of vs from a decimal numeral, if I can, to the integer the numeral represents, and return a vector of those integers. Without prejudice to other policies I'll throw an std::invalid_argument exception when an element doesn't trim to a decimal numeral.

I am spoilt for choice as to what you might mean by converting an std::vector<std::string> to bool, so I will arbitrarily say that vs is true if I can convert it to a vector<int> of which any element is non-0 and is false if I can convert it to a vector<int> in which all elements are 0.

The sketch:

#include <type_traits>
#include <map>
#include <vector>
#include <string>
#include <algorithm>

namespace detail {

template<typename T>
struct convert_to
{
    static T from(std::vector<std::string> const & vs) {
        static constexpr bool always_false = !std::is_same<T,T>::value;
        static_assert(always_false,
            "Calling `convert_to<T>::from` for unimplemented `T`");
        return *(T*)nullptr;
    }
};

template<>
std::string convert_to<std::string>::from(std::vector<std::string> const & vs)
{
    std::string s;
    for (  auto const & e : vs ) {
        s += e;
    }
    return s;
}

template<>
std::vector<int> 
convert_to<std::vector<int>>::from(std::vector<std::string> const & vs)
{
    auto lamb = [](std::string const & s) {
        std::size_t lastoff = s.find_last_not_of(" \t\f\v\n\r");
        int i;
        try {
            std::size_t nlen;
            i = std::stoi(s,&nlen);
            if (nlen <= lastoff) {
                throw std::invalid_argument("");
            }
        }
        catch(std::invalid_argument const & e) {
            throw std::invalid_argument(
                "Cannot convert \"" + s + "\" to int");
        }
        return i;
    };
    std::vector<int> vi;
    std::transform(vs.begin(),vs.end(),std::back_inserter(vi),lamb);
    return vi;
}

template<>
bool convert_to<bool>::from(std::vector<std::string> const & vs)
{
    auto vi = convert_to<std::vector<int>>::from(vs);
    for (auto const & i : vi) {
        if (i) {
            return true;
        }
    }
    return false;

}

} // namespace detail


struct myClass // Your class
{
    // Whatever...

    std::vector<std::string> & operator[](std::string const & key) {
        return _map[key];
    }

    template<typename T>
    T as(std::string const & key) {
        return detail::convert_to<T>::from(_map[key]);
    }

    // Whatever...

private:
    std::map<std::string,std::vector<std::string>> _map;
};

The one take-away point here is the use of template<typename T> detail::struct convert_to, with its solitary static member function:

T from(std::vector<std::string> const & vs)

which in the default instantiation will provoke a static_assert failure reporting that no conversion to T from std::vector<std::string> has been defined.

Then, for each type U to which you want a conversion, you have just to write a specializing definition:

template<>
U convert_to<U>::from(std::vector<std::string> const & vs);

as you see fit, and the construction (1c) will use it as per:

template<typename T>
T myClass::as(std::string const & key) {
    return detail::convert_to<T>::from(_map[key]);
}

Here's an illustrative progam you can append to the sketch:

#include <iostream>

using namespace std;

template<typename T>
static void print_vec(std::vector<T> const & v)
{
    cout << "{ ";
    for (auto const & e : v) {
        cout << e << " ";
    }
    cout << "}\n";
}

static void print_vec(std::vector<std::string> const & v)
{
    cout << "{ ";
    for (auto const & e : v) {
        cout << '\"' << e << "\" ";
    }
    cout << "}\n";
}

int main()
{
    myClass f;
    f["int_vec"] = vector<string>{"0","1 "," 2"};
    cout << "f[\"int_vec\"] = "; print_vec(f["int_vec"]); 
    f["true_vec"] = vector<string>{"0"," 1 ","0"};
    cout << "f[\"true_vec\"] = "; print_vec(f["true_vec"]);
    f["false_vec"] = vector<string>{"0"," 0","0 "};
    cout << "f[\"false_vec\"] = "; print_vec(f["false_vec"]);
    f["not_int_vec0"] = vector<string>{"0","1","2",""};
    cout << "f[\"not_int_vec0\"] = "; print_vec(f["not_int_vec0"]);
    f["not_int_vec1"] = vector<string>{"0","@","2",};
    cout << "f[\"not_int_vec1\"] = "; print_vec(f["not_int_vec1"]);
    f["not_int_vec2"] = vector<string>{"0"," 1$","2",};
    cout << "f[\"not_int_vec2\"] = "; print_vec(f["not_int_vec2"]);
    cout << "f.as<string>(\"int_vec\") = \"" 
        << f.as<string>("int_vec") << '\"' << endl;
    cout << "f.as<string>(\"true_vec\") = \"" 
        << f.as<string>("true_vec") << '\"' << endl;
    cout << "f.as<string>(\"false_vec\") = \"" 
        << f.as<string>("false_vec") << '\"' << endl;
    cout << "f.as<string>(\"not_int_vec0\") = \"" 
        << f.as<string>("not_int_vec0") << '\"' << endl;
    cout << "f.as<string>(\"not_int_vec1\") = \""
        << f.as<string>("not_int_vec1") << '\"' << endl;
    cout << "f.as<string>(\"not_int_vec2\") = \""
        << f.as<string>("not_int_vec2") << '\"' << endl;
    vector<int> va = f.as<vector<int>>("int_vec");
    cout << "f.as<vector<int>>(\"int_vec\") = "; 
    print_vec(f.as<vector<int>>("int_vec"));
    cout << boolalpha << "f.as<bool>(\"true_vec\") = " 
        << f.as<bool>("true_vec") << endl;
    cout << boolalpha << "f.as<bool>(\"false_vec\") = " 
        << f.as<bool>("false_vec") << endl;
    try {
        cout << "f.as<vector<int>>(\"not_int_vec0\")...";
        auto b = f.as<vector<int>>("not_int_vec0");
        (void)b;
    }
    catch(std::invalid_argument const & e) {
        cout << e.what() << endl;
    }
    try {
        cout << "f.as<vector<int>>(\"not_int_vec1\")...";
        auto b = f.as<vector<int>>("not_int_vec1");
        (void)b;
    }
    catch(std::invalid_argument const & e) {
        cout << e.what() << endl;
    }
    try {
        cout << "f.as<vector<int>>(\"not_int_vec2\")...";
        auto b = f.as<vector<int>>("not_int_vec2");
        (void)b;
    }
    catch(std::invalid_argument const & e) {
        cout << e.what() << endl;
    }
    // char ch = f.as<char>("int_vec"); <- static_assert fails
    return 0;
}

It outputs:

f["int_vec"] = { "0" "1 " " 2" }
f["true_vec"] = { "0" " 1 " "0" }
f["false_vec"] = { "0" " 0" "0 " }
f["not_int_vec0"] = { "0" "1" "2" "" }
f["not_int_vec1"] = { "0" "@" "2" }
f["not_int_vec2"] = { "0" " 1$" "2" }
f.as<string>("int_vec") = "01  2"
f.as<string>("true_vec") = "0 1 0"
f.as<string>("false_vec") = "0 00 "
f.as<string>("not_int_vec0") = "012"
f.as<string>("not_int_vec1") = "0@2"
f.as<string>("not_int_vec2") = "0 1$2"
f.as<vector<int>>("int_vec") = { 0 1 2 }
f.as<bool>("true_vec") = true
f.as<bool>("false_vec") = false
f.as<vector<int>>("not_int_vec0")...Cannot convert "" to int
f.as<vector<int>>("not_int_vec1")...Cannot convert "@" to int
f.as<vector<int>>("not_int_vec2")...Cannot convert " 1$" to int

(gcc 5.1, clang 3.6, C++11)

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.