In c++11 code it would be nice to avoid mentioning a specific enum qualifier every time I use the enum value - as it is a new code and it is refactored a lot.
For that purpose is it possible something in the spirit of the last line of this pseudo code:
enum abc { a,b,c };
// some long code of events which returns the enum's value
auto e = []()->abc{return abc::b;}();
if (e == std::declval(e)::a) { ...
If not possible in C++11 will it become possible in C++14 or 17?
You're close, you can use decltype:
if (e == decltype(e)::a) {
...
Related
I'm using the ranges library to help filer data in my classes, like this:
class MyClass
{
public:
MyClass(std::vector<int> v) : vec(v) {}
std::vector<int> getEvens() const
{
auto evens = vec | ranges::views::filter([](int i) { return ! (i % 2); });
return std::vector<int>(evens.begin(), evens.end());
}
private:
std::vector<int> vec;
};
In this case, a new vector is constructed in the getEvents() function. To save on this overhead, I'm wondering if it is possible / advisable to return the range directly from the function?
class MyClass
{
public:
using RangeReturnType = ???;
MyClass(std::vector<int> v) : vec(v) {}
RangeReturnType getEvens() const
{
auto evens = vec | ranges::views::filter([](int i) { return ! (i % 2); });
// ...
return evens;
}
private:
std::vector<int> vec;
};
If it is possible, are there any lifetime considerations that I need to take into account?
I am also interested to know if it is possible / advisable to pass a range in as an argument, or to store it as a member variable. Or is the ranges library more intended for use within the scope of a single function?
This was asked in op's comment section, but I think I will respond it in the answer section:
The Ranges library seems promising, but I'm a little apprehensive about this returning auto.
Remember that even with the addition of auto, C++ is a strongly typed language. In your case, since you are returning evens, then the return type will be the same type of evens. (technically it will be the value type of evens, but evens was a value type anyways)
In fact, you probably really don't want to type out the return type manually: std::ranges::filter_view<std::ranges::ref_view<const std::vector<int>>, MyClass::getEvens() const::<decltype([](int i) {return ! (i % 2);})>> (141 characters)
As mentioned by #Caleth in the comment, in fact, this wouldn't work either as evens was a lambda defined inside the function, and the type of two different lambdas will be different even if they were basically the same, so there's literally no way of getting the full return type here.
While there might be debates on whether to use auto or not in different cases, but I believe most people would just use auto here. Plus your evens was declared with auto too, typing the type out would just make it less readable here.
So what are my options if I want to access a subset (for instance even numbers)? Are there any other approaches I should be considering, with or without the Ranges library?
Depends on how you would access the returned data and the type of the data, you might consider returning std::vector<T*>.
views are really supposed to be viewed from start to end. While you could use views::drop and views::take to limit to a single element, it doesn't provide a subscript operator (yet).
There will also be computational differences. vector need to be computed beforehand, where views are computed while iterating. So when you do:
for(auto i : myObject.getEven())
{
std::cout << i;
}
Under the hood, it is basically doing:
for(auto i : myObject.vec)
{
if(!(i % 2)) std::cout << i;
}
Depends on the amount of data, and the complexity of computations, views might be a lot faster, or about the same as the vector method. Plus you can easily apply multiple filters on the same range without iterating through the data multiple times.
In the end, you can always store the view in a vector:
std::vector<int> vec2(evens.begin(), evens.end());
So my suggestions is, if you have the ranges library, then you should use it.
If not, then vector<T>, vector<T*>, vector<index> depending on the size and copiability of T.
There's no restrictions on the usage of components of the STL in the standard. Of course, there are best practices (eg, string_view instead of string const &).
In this case, I can foresee no problems with handling the view return type directly. That said, the best practices are yet to be decided on since the standard is so new and no compiler has a complete implementation yet.
You're fine to go with the following, in my opinion:
class MyClass
{
public:
MyClass(std::vector<int> v) : vec(std::move(v)) {}
auto getEvens() const
{
return vec | ranges::views::filter([](int i) { return ! (i % 2); });
}
private:
std::vector<int> vec;
};
As you can see here, a range is just something on which you can call begin and end. Nothing more than that.
For instance, you can use the result of begin(range), which is an iterator, to traverse the range, using the ++ operator to advance it.
In general, looking back at the concept I linked above, you can use a range whenever the conext code only requires to be able to call begin and end on it.
Whether this is advisable or enough depends on what you need to do with it. Clearly, if your intention is to pass evens to a function which expects a std::vector (for instance it's a function you cannot change, and it calls .push_back on the entity we are talking about), you clearly have to make a std::vector out of filter's output, which I'd do via
auto evens = vec | ranges::views::filter(whatever) | ranges::to_vector;
but if all the function which you pass evens to does is to loop on it, then
return vec | ranges::views::filter(whatever);
is just fine.
As regards life time considerations, a view is to a range of values what a pointer is to the pointed-to entity: if the latter is destroied, the former will be dangling, and making improper use of it will be undefined behavior. This is an erroneous program:
#include <iostream>
#include <range/v3/view/filter.hpp>
#include <string>
using namespace ranges;
using namespace ranges::views;
auto f() {
// a local vector here
std::vector<std::string> vec{"zero","one","two","three","four","five"};
// return a view on the local vecotor
return vec | filter([](auto){ return true; });
} // vec is gone ---> the view returned is dangling
int main()
{
// the following throws std::bad_alloc for me
for (auto i : f()) {
std::cout << i << std::endl;
}
}
You can use ranges::any_view as a type erasure mechanism for any range or combination of ranges.
ranges::any_view<int> getEvens() const
{
return vec | ranges::views::filter([](int i) { return ! (i % 2); });
}
I cannot see any equivalent of this in the STL ranges library; please edit the answer if you can.
EDIT: The problem with ranges::any_view is that it is very slow and inefficient. See https://github.com/ericniebler/range-v3/issues/714.
It is desirable to declare a function returning a range in a header and define it in a cpp file
for compilation firewalls (compilation speed)
stop the language server from going crazy
for better factoring of the code
However, there are complications that make it not advisable:
How to get type of a view?
If defining it in a header is fine, use auto
If performance is not a issue, I would recommend ranges::any_view
Otherwise I'd say it is not advisable.
I want to check at compile time if std::span has a specific layout. The code below is what I've got so far. It doesn't work.
#include<bit>
#include<cassert>
#include<iostream>
#include<span>
#include<type_traits>
#include<vector>
template<typename T>
struct MySpan {
T* data;
std::size_t size;
};
int main() {
assert(std::is_standard_layout_v<MySpan<int>>);
assert(std::is_standard_layout_v<std::span<int>>);
std::vector<int> v{ 1, 2, 3 };
std::span const sp{ v };
auto const mySpan = std::bit_cast<MySpan<int>>(sp);
assert(sp.data() == mySpan.data);
assert(sp.size() == mySpan.size);
assert(sizeof(sp) == sizeof(mySpan));
constexpr bool layoutCompat = std::is_layout_compatible_v<std::span<int>, MySpan<int>>;
assert(layoutCompat); // assertion failed, why?
}
This:
std::is_layout_compatible_v<std::span<int>, MySpan<int>>;
Should evaluate as true with MSVC's implementation. It does lay out the fields in that order. That fact that it does not is an MSVC bug, which I reported here and then Casey Carter moved to here.
Casey's reduced version of my example from the test case there is (but rewritten by me to avoid using the compiler intrinsic):
#include <type_traits>
struct B { int x; };
class D : public B {};
static_assert(std::is_layout_compatible_v<D, B>);
This should be valid (B and D are layout-compatible), but the compiler currently rejects this. This is the same thing that's causing the original span check to fail.
Without reflection, there is no mechanism you can use to determine the layout of a type. You can test layout compatibility, but you cannot determine the layout itself. That is, if they're not layout compatible, you can't determine why they aren't.
Maybe that span implementation puts the size first. Maybe the size is in a base class subobject which can be empty when the size is static. Who knows.
You cannot write code to bit_cast a span. Not code that works across implementations, anyway. And really, there's no point in doing so. You can just give your type the ability to construct itself from an existing span. bit_cast is returning a new object anyway, so it's not like it's going to be faster or something.
Let's say I have a strongly typed enum type like this:
enum class message : int {
JOIN = 0,
LEAVE = 4,
SPAWN = 1,
}
And I need to safely (safely in this case means discarding invalid variants) convert it from it's underlying type (int).
For this purpose, I have a function to convert it for me:
std::optional<message> get_message(int num) {
return num == (int)message::JOIN || num == (int)message::LEAVE || num == (int)message::SPAWN ? (message)num : {};
}
This works, but is long to write and prone to mistakes, especially for enums with a larger number of variants.
Is there a way to automate this process in C++17?
Talking about underlying type, we notice that this class merely obtains a type using another type as model, but it does not transform values or objects between those types.
As an option to simplify the function you could work by iterating in the enum,or as others said before with some type of container, by iterating the same enum as an example here: How can I iterate over an enum?
and more information about enum just in case: https://learn.microsoft.com/en-us/cpp/cpp/enumerations-cpp?view=vs-2019
When a function need to return two parameters you can write it using a std::pair:
std::pair<int, int> f()
{return std::make_pair(1,2);}
And if you want to use it, you can write this:
int one, two;
std::tie(one, two) = f();
The problem with this approach is that you need to define 'one' and 'two' and then assign them to the return value of f(). It would be better if we can write something like
auto {one, two} = f();
I watched a lecture (I don't remember which one, sorry) in which the speaker said that the C++ standard people where trying to do something like that. I think this lecture is from 2 years ago. Does anyone know if right now (in almost c++17) you can do it or something similar?
Yes, there is something called structured bindings that allows for multiple values to be initialized in that way.
The syntax uses square brackets however:
#include <utility>
std::pair<int, int> f()
{
//return std::make_pair(1, 2); // also works, but more verbose
return { 1, 2 };
}
int main()
{
auto[one, two] = f();
}
demo
Ok. this is my code :
CShop::~CShop()
{
TPacketGCShop pack;
pack.header = HEADER_GC_SHOP;
pack.subheader = SHOP_SUBHEADER_GC_END;
pack.size = sizeof(TPacketGCShop);
Broadcast(&pack, sizeof(pack));
GuestMapType::iterator it;
it = m_map_guest.begin();
while (it != m_map_guest.end())
{
LPCHARACTER ch = it->first;
ch->SetShop(NULL);
++it;
}
M2_DELETE(m_pGrid);
}
Soo i have GuestMapType::iterator it; and this it = m_map_guest.begin();
It'f fine if i make my function like this?
CShop::~CShop()
{
TPacketGCShop pack;
pack.header = HEADER_GC_SHOP;
pack.subheader = SHOP_SUBHEADER_GC_END;
pack.size = sizeof(TPacketGCShop);
Broadcast(&pack, sizeof(pack));
auto it = m_map_guest.begin();
while (it != m_map_guest.end())
{
LPCHARACTER ch = it->first;
ch->SetShop(NULL);
++it;
}
M2_DELETE(m_pGrid);
}
I removed GuestMapType::iterator it; to simplify my code?
My question is. Affect this my program? Any risk ?
That's perfectly fine and declaring iterators with auto is, from my view, a good practice for at least two reasons:
1- Generally, iterator's type are pretty long to type. The less you type, the less you mis-type. It makes the code clearer too, because you hide an implementation detail that doesn't really matter, in that context.
2- Forward compatibility: when you will modify your code, i.e. the name for the iterator type name, you won't have to change the code where you use it with auto. After all, you want to use that type of iterator, independently from its name.
Provided the return type of m_map_guest.begin() can be determined to be GuestMapType::iterator, there should be no difference.
The auto typing is not some magical dynamic/weak type, it's simply an indicator to the compiler that it should work out the strong type itself, without you being explicit about it1. There is no functional difference between the two lines:
int i = 7;
auto i = 7;
because the compiler uses the type of the 7 in the second example to infer the actual type of i.
You should generally use auto wherever possible for the same reason you should use the second of these in C:
ttype *x = malloc(sizeof (ttype));
ttype *x = malloc(sizeof (*x));
The latter requires only one change if the type ever changes to something else - that becomes much more important with auto as the actual type and declaration can be much more widely separated.
1 Of course, where the return type is different, such as implicit conversions or sub-classes, it's a little harder for the compiler to work out what type you want. So it may not work in those cases.