I found this code on cppreference.com. I was wondering if boost provides a similar function for its variant type. I found the boost documentation really awful and can't find anything.
int main()
{
std::variant<int, std::string> v = "abc";
std::cout << std::boolalpha
<< "variant holds int? "
<< std::holds_alternative<int>(v) << '\n'
<< "variant holds string? "
<< std::holds_alternative<std::string>(v) << '\n';
}
Although not exactly the same, you can use the pointer based get function:
boost::variant<int, std::string> v = "abc";
std::cout << std::boolalpha
<< "variant holds int? "
<< (boost::get<int>(&v) != nullptr) << '\n'
<< "variant holds string? "
<< (boost::get<std::string>(&v) != nullptr) << '\n';
You can create a simple wrapper that will work just like the standard one. Use the fact that boost::get has multiple overloads and when passed a pointer, it will also return a (possibly null) pointer.
template <typename T, typename... Ts>
bool holds_alternative(const boost::variant<Ts...>& v) noexcept
{
return boost::get<T>(&v) != nullptr;
}
It will be also picked up by ADL, so it doesn't matter much where you put it.
No but, you can use the type() method:
#include <iostream>
#include <boost/variant.hpp>
int main()
{
boost::variant<int, std::string> v = "abc";
std::cout << std::boolalpha
<< "variant holds int? "
<< (v.type() == typeid(int)) << '\n'
<< "variant holds string? "
<< (v.type() == typeid(std::string)) << '\n';
}
But it will not protect you against having the same type twice (boost::variant<int, int, std::string>) as std::holds_alternative would do.
Related
Here is my code:
bool isNotValid (char a) {
if (isalpha(a) || a == '_')
{
cout << "\n- isalpha";
return 0;
}
else
{
cout << "\n- notalpha";
return 1;
}
}
bool test123(const string& test)
{
return find_if(test.begin(), test.end(), isNotValid) != test.end();
}
int main()
{
string test;
cout << "Test input: ";
cin >> test;
if (!test123(test))
cout << "\n- Valid\n";
else
cout << "\n- Not Valid\n";
return 0;
}
This is part of my code to check the validity of username in my program. I don't really understand what exactly I am iterating through when I insert the string into my function as address of the string. CPP reference states that find_if iterates from first to last position of a sequence.
Poked through the code with cout at different location, still didn't quite catch what is going on.
You are iterating your string. You did not pass the address of the string. The function takes the string as a reference to const, meaning it passes the actual string (no copy is made) and the function is not allowed to modify the string. You are iterating character by character in your string and calling your function isNotValid() on each character.
Notes:
Instead of returning 1 or 0 from isNotValid(), return true or false.
Consider flipping your logic and renaming the function to isValid() instead. You would also have to change test123() to use std::find_if_not(). Finally, you would check if the returned iterator is end() and not if it's not.
But, if you do change isNotValid() to isValid(), you'd be better off switching from std::find_if() entirely to to std::all_of(). It makes more sense, is more readable, and returns a bool directly (No need to compare against end()).
But if you want to keep your function isNotValid(), the comment that suggests using std::any_of() is what I would recommend for the same reasons.
Here's my take on your code:
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
bool isValid(char a) {
return std::isalpha(static_cast<unsigned char>(a)) || a == '_'; // !
}
bool test123(const std::string& test) {
return std::all_of(test.begin(), test.end(), isValid); // !
}
int main() {
std::string testOne{"i_am_valid"};
std::string testTwo{"i_am_invalid_123"};
std::cout << "Testing: " << testOne << " : " << std::boolalpha
<< test123(testOne) << '\n';
std::cout << "Testing: " << testTwo << " : " << std::boolalpha
<< test123(testTwo) << '\n';
}
Output:
❯ ./a.out
Testing: i_am_valid : true
Testing: i_am_invalid_123 : false
I would argue that readability has stayed largely the same, but the mental load has been shifted; the Boolean flips make a bit more sense.
As you progress in your learning, you might not even want to have the function isValid() if it's a one-off thing. C++11 introduced lambdas, or functions as objects. C++20 also introduced ranges, so you don't have to pass a pair of iterators if you intend to iterate the whole container anyway.
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
bool test123(const std::string& test) {
return std::ranges::all_of(test, [](const auto& c) {
return std::isalpha(static_cast<unsigned char>(c)) || c == '_';
}); // !
}
int main() {
std::string testOne{"i_am_valid"};
std::string testTwo{"i_am_invalid_123"};
std::cout << "Testing: " << testOne << " : " << std::boolalpha
<< test123(testOne) << '\n';
std::cout << "Testing: " << testTwo << " : " << std::boolalpha
<< test123(testTwo) << '\n';
}
That's a bit hairy to read if you're not familiar with lambdas, but I find lambdas useful for checks like this where you're just doing it the one time.
How can I pack in a vector several different primitive data types as well as strings to examine the contents then?
(In Java, this went via Object [] objects = {1.0, "Hello", - 42, 'b'})
The task is as follows:
Given the following array: [3.0,42, "Monkey", 7.2, b]
This array is to be passed to a method that outputs the contents of the array on the console. If it is a string, each letter of the string should be added as an ASCII value in the same variable, and finally returned as an int on the console. With char exactly the same.
I know from today as I create a vector so with std::vector<double> numbers = {1.0,2.0}; How to write functions and how to access the indexes numbers[i]; as well as the length of the vector numbers.size().
How can I solve this problem now? Since I have unfortunately found no simple <- solution for the multiple types in a vector.
Thanks in advance :)
C++ does not do type erasure the same way that Java does. To create a heterogeneous container (which is the technical term for what you're trying to do) you'll need to make extensive use of std::any or std::variant, which are new classes introduced with C++17.
std::vector<std::any> values{1.0, "Hello", -42, 'b'};
for(auto & any : values) {
int * i;
if(val = std::any_cast<int>(&any)) std::cout << "int: " << *i << std::endl;
const char ** s;
if(s = std::any_cast<const char *>(&any)) std::cout << "string-literal: " << *s << std::endl;
double * d;
if(d = std::any_cast<double>(&any)) std::cout << "double: " << *d << std::endl;
char * c;
if(c = std::any_cast<char>(&any)) std::cout << "char: " << *c << std::endl;
}
Note how messy that code is. Not least of which because many people would desire "hello" to be stored as a std::string object, but this can't be done unless the user expressly designates it as such:
std::vector<std::any> values{1.0, std::string{"Hello"}, -42, 'b'};
At any rate, my personal opinion is that the use of std::variant would be a much better fit, as you can make it much clearer how the container is meant to be used, and you can avoid the dynamic allocations associated with std::any:
typedef std::variant<std::string, char, double, int> my_variant;
struct visitor {
void operator()(std::string const& v) const {
std::cout << "std::string: " << v<< std::endl;
}
void operator()(double const& v) const {
std::cout << "double: " << v << std::endl;
}
void operator()(int const& v) const {
std::cout << "int: " << v << std::endl;
}
void operator()(char const& v) const {
std::cout << "char: " << v << std::endl;
}
};
int main() {
std::vector<my_variant> values{1.0, "Hello", -42, 'b'};
for(my_variant & variant : values) {
std::visit(visitor{}, variant);
}
return 0;
}
We can even make the variant version a lot simpler with auto lambdas if we don't need to know the type:
typedef std::variant<std::string, char, double, int> my_variant;
int main() {
std::vector<my_variant> values{1.0, "Hello", -42, 'b'};
for(my_variant & variant : values) {
std::visit(
[](auto const& val) {std::cout << "Some unknown type: " << val << std::endl;},
variant
);
}
return 0;
}
I haven't run this through my compiler, but this should give a pretty good sense of how to accomplish this kind of task in C++.
If you don't have access to C++17, you can use boost.any and boost.variant, which I'm reasonably sure are both header-only libraries, and thus easy to import into your project.
My question is that of safety. I've searched cplusplus.com and cppreference.com and they seem to be lacking on iterator safety during std::move. Specifically: is it safe to call std::unordered_map::erase(iterator) with an iterator whose object has been moved? Sample code:
#include <unordered_map>
#include <string>
#include <vector>
#include <iostream>
#include <memory>
class A {
public:
A() : name("default ctored"), value(-1) {}
A(const std::string& name, int value) : name(name), value(value) { }
std::string name;
int value;
};
typedef std::shared_ptr<const A> ConstAPtr;
int main(int argc, char **argv) {
// containers keyed by shared_ptr are keyed by the raw pointer address
std::unordered_map<ConstAPtr, int> valued_objects;
for ( int i = 0; i < 10; ++i ) {
// creates 5 objects named "name 0", and 5 named "name 1"
std::string name("name ");
name += std::to_string(i % 2);
valued_objects[std::make_shared<A>(std::move(name), i)] = i * 5;
}
// Later somewhere else we need to transform the map to be keyed differently
// while retaining the values for each object
typedef std::pair<ConstAPtr, int> ObjValue;
std::unordered_map<std::string, std::vector<ObjValue> > named_objects;
std::cout << "moving..." << std::endl;
// No increment since we're using .erase() and don't want to skip objects.
for ( auto it = valued_objects.begin(); it != valued_objects.end(); ) {
std::cout << it->first->name << "\t" << it->first.value << "\t" << it->second << std::endl;
// Get named_vec.
std::vector<ObjValue>& v = named_objects[it->first->name];
// move object :: IS THIS SAFE??
v.push_back(std::move(*it));
// And then... is this also safe???
it = valued_objects.erase(it);
}
std::cout << "checking... " << named_objects.size() << std::endl;
for ( auto it = named_objects.begin(); it != named_objects.end(); ++it ) {
std::cout << it->first << " (" << it->second.size() << ")" << std::endl;
for ( auto pair : it->second ) {
std::cout << "\t" << pair.first->name << "\t" << pair.first->value << "\t" << pair.second << std::endl;
}
}
std::cout << "double check... " << valued_objects.size() << std::endl;
for ( auto it : valued_objects ) {
std::cout << it.first->name << " (" << it.second << ")" << std::endl;
}
return 0;
}
The reason I ask is that it strikes me that moving the pair from the unordered_map's iterator may (?) therefore *re*move the iterator's stored key value and therefore invalidate its hash; therefore any operations on it afterward could result in undefined behavior. Unless that's not so?
I do think it's worth noting that the above appears to successfully work as intended in GCC 4.8.2 so I'm looking to see if I missed documentation supporting or explicitly not supporting the behavior.
// move object :: IS THIS SAFE??
v.push_back(std::move(*it));
Yes, it is safe, because this doesn't actually modify the key. It cannot, because the key is const. The type of *it is std::pair<const ConstAPtr, int>. When it is moved, the first member (the const ConstAPtr) is not actually moved. It is converted to an r-value by std::move, and becomes const ConstAPtr&&. But that doesn't match the move constructor, which expects a non-const ConstAPtr&&. So the copy constructor is called instead.
I am using boost spirit to parse some text files into a data structure and now I am beginning to generate text from this data structure (using spirit karma).
One attempt at a data structure is a boost::fusion::map (as suggested in an answer to
this question). But although I can use boost::spirit::qi::parse() and get data in it easily, when I tried to generate text from it using karma, I failed.
Below is my attempt (look especially at the "map_data" type). After some reading and playing around with other fusion types, I found boost::fusion::vector and BOOST_FUSION_DEFINE_ASSOC_STRUCT. I succeeded to generate output with both of them, but they don't seem ideal: in vector you cannot access a member using a name (it is like a tuple) -- and in the other solution, I don't think I need both ways (member name and key type) to access the members.
#include <iostream>
#include <string>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/map.hpp>
#include <boost/fusion/include/make_map.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/fusion/include/transform.hpp>
struct sb_key;
struct id_key;
using boost::fusion::pair;
typedef boost::fusion::map
< pair<sb_key, int>
, pair<id_key, unsigned long>
> map_data;
typedef boost::fusion::vector < int, unsigned long > vector_data;
#include <boost/fusion/include/define_assoc_struct.hpp>
BOOST_FUSION_DEFINE_ASSOC_STRUCT(
(), assocstruct_data,
(int, a, sb_key)
(unsigned long, b, id_key))
namespace karma = boost::spirit::karma;
template <typename X>
std::string to_string ( const X& data )
{
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
karma::generate_delimited ( sink, karma::int_ << karma::ulong_, karma::space, data );
return generated;
}
int main()
{
map_data d1(boost::fusion::make_map<sb_key, id_key>(234, 35314988526ul));
vector_data d2(boost::fusion::make_vector(234, 35314988526ul));
assocstruct_data d3(234,35314988526ul);
std::cout << "map_data as_vector: " << boost::fusion::as_vector(d1) << std::endl;
//std::cout << "map_data to_string: " << to_string(d1) << std::endl; //*FAIL No 1*
std::cout << "at_key (sb_key): " << boost::fusion::at_key<sb_key>(d1) << boost::fusion::at_c<0>(d1) << std::endl << std::endl;
std::cout << "vector_data: " << d2 << std::endl;
std::cout << "vector_data to_string: " << to_string(d2) << std::endl << std::endl;
std::cout << "assoc_struct as_vector: " << boost::fusion::as_vector(d3) << std::endl;
std::cout << "assoc_struct to_string: " << to_string(d3) << std::endl;
std::cout << "at_key (sb_key): " << boost::fusion::at_key<sb_key>(d3) << d3.a << boost::fusion::at_c<0>(d3) << std::endl;
return 0;
}
Including the commented line gives lots of pages of compilation errors, among which notably something like:
no known conversion for argument 1 from ‘boost::fusion::pair’ to ‘double’
no known conversion for argument 1 from ‘boost::fusion::pair’ to ‘float’
Might it be that to_string needs the values of the map_data, and not the pairs? Though I am not good with templates, I tried to get a vector from a map using transform in the following way
template <typename P>
struct take_second
{
typename P::second_type operator() (P p)
{
return p.second;
}
};
// ... inside main()
pair <char, int> ff(32);
std::cout << "take_second (expect 32): "
<< take_second<pair<char,int>>()(ff) << std::endl;
std::cout << "transform map_data and to_string: "
<< to_string(boost::fusion::transform(d1, take_second<>())); //*FAIL No 2*
But I don't know what types am I supposed to give when instantiating take_second and anyway I think there must be an easier way to get (iterate over) the values of a map (is there?)
If you answer this question, please also give your opinion on whether using an ASSOC_STRUCT or a map is better.
I think I noticed your question on the [spirit-general] list earlier.
It got 14 views there - and I did some fairly deep investigation. Sadly, to the best of my knowledge I don't think Spirit has any support for associate Fusion structures.
In fact, outside MSM and Phoenix, there was no place in boost where I see fusion::map being used.
Is there a chance you could just use std::map/std::pair instead? Here's a tiny proof of concept:
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/adapted.hpp>
int main()
{
const auto data = std::map<std::string, double> {
{ "pi", 3.1415925 },
{ "e", 2.718281828 },
{ "Answer", 42 } };
namespace karma = boost::spirit::karma;
std::cout << karma::format((karma::string << " = " << karma::double_) % karma::eol, data)
<< std::endl;
}
Output:
Answer = 42.0
e = 2.718
pi = 3.142
I'm trying to write a copy constructor for an object managing a STL map containing pointers, where the key is a string. However, when I attempt to insert new values in the map, the pointers are set to NULL:
// ...
for(std::map<std::string, data_base*, order>::const_iterator it = other.elements.begin();
it != other.elements.end(); ++it){
data_base *t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
elements[it->first] = t;
std::cout << "INSERTED: " << std::hex << elements[it->first] << std::endl;
}
// ...
other is the object being copied and elements the map. The clone() method returns a pointer to a new object (via new).
Running the code above I get something like:
CLONE: 0xcfbbc0
INSERTED: 0
I'm not a very experienced programmer and this issue is probably simple to fix, but I didnt find any solution to it searching around.
Thanks a lot for your time.
I don't see any problem with this code, other than maybe
std::map<std::string, data_base*, order>::const_iterator it
Here order gives the key comparator to use to sort the pairs contained in the map (often implemented as a tree).
Maybe you're doing something wrong in it, making your [] operator don't find the right ke, making your last line logging a new pair with a null ptr.
First, try without that order, using the default key-comparator (std::less), then if it don't work, post your order definition and the map declaration. If it's not enough, just provide a simple complete program that reproduce the problem.
I just wrote a simple similar test, using the default key-comparator :
#include <map>
#include <string>
#include <iostream>
struct Data
{
int k;
Data* clone() { return new Data(); }
};
typedef std::map< std::string, Data* > DataMap;
DataMap data_map;
int main()
{
data_map[ "hello" ] = new Data();
data_map[ "world" ] = new Data();
DataMap other_map;
for( DataMap::const_iterator it = data_map.begin(); it != data_map.end(); ++it)
{
Data*t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
other_map[it->first] = t;
std::cout << "INSERTED: " << std::hex << other_map[it->first] << std::endl;
}
std::cin.ignore();
return 0;
}
On VS2010SP1, this outputs :
CLONE: 00034DD0
INSERTED: 00034DD0
CLONE: 00035098
INSERTED: 00035098
So it should be the problem, or maybe you're doing something wrong before.
Try this out, to help debug the issue. I'd recommend double-checking that the order function is correct. You can remove it to use std::less<T>, which is known to work.
// ...
typedef std::map<std::string, data_base*, order> string_db_map;
for(string_db_map::const_iterator it = other.elements.begin();
it != other.elements.end();
++it)
{
data_base *t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
std::pair<string_db_map::iterator, bool) result = elements.insert(
string_db_map::value_type( it->first, t));
if ( !result.second )
{
std::cout << "element['" << it->first << "'] was already present, and replaced." << std::endl;
}
std::coud << "INSERTED [iterator]: " << std::hex << (*result.first).second << std::endl;
std::cout << "INSERTED [indexed]: " << std::hex << elements[it->first] << std::endl;
}
// ...