c++11 include base constructor (etc) in derived class
|
The awesome C++11 way to steal the constructor from the base class, oh man I've been waiting for this one...
class HttpsServer : public Server<HTTPS>
{
// DOES THIS FOR YOU!
/*
HttpsServer(unsigned short port, size_t num_threads, const std::string& cert_file, const std::string& private_key_file)
:
// Call base class
Server<HTTPS>::Server(port, num_threads, cert_file, private_key_file)
{}
*/
using Server<HTTPS>::Server;
....
|
c++11 containers
|
sorted_vector |
use when doing lots of unsorted insertions and maintaining constant sort would be expensive; vector is good for a big pile of things that only occasionally needs a sorted lookup
|
map |
sorted binary search tree; always sorted by key; you can walk through in sorted order (choose unordered if not needed!)
|
multimap |
same as map but allows dupe keys (not as common)
|
unordered_map |
hashmap; always sorted by key; additional bucket required for hash collisions; no defined order when walking through
|
unordered_multimap |
same as map but allows dupe keys; dupes are obviously in the same bucket, and you can walk just the dupes if needed
|
set multiset unordered_set unordered_multiset |
sets are just like maps, except the key is embedded in the object, nice for encapsulation.
Items must be const (!) since they are the key - sounds bad, but this is mitigated by the mutable keyword.
You can use mutable on the variables that are not part of the key to remove the const.
This changes the constness of the object from binary (completely const) to logical (constness is defined by the developer).
So... set is a good way to achieve both encapsulation and logical const - make const work for you, not against! :-)
|
set (etc.) of pointers |
sets of pointers are the pinnacle of object stores
The entire object can be dereferenced and accessed then without const issues.
A pointer functor can be provided that does a sort by dereferencing the pointer to the object.
Two requirements: you must make sure yourself that you do not change the key values - you can mark them const, provided in constructor;
you must create sort/equal/hash functors that dereference the pointers to use object contents
(the default will be by pointer address).
The arguably biggest advantage, as a result, is that you can create multiple sets
to reference the same group of objects with different sort funtors to create multiple indices.
You just have to manage the keys carefully, so that they don't change (which would invalidate the sorting).
The primary container can manage object allocation; using a heap-based unique_ptr allocation
map vs key redux
use a key in the set, derive a class from it with the contents
+ small key
+ encapsulation
- requires mutable to solve the const problem
use a key in the set, key includes a mutable object
+ encapsulation
- weird bc everything uses a const object but we have const functions like save() that change the mutable subobject
use a map
+ small key
- no encapsulation, have to deal with a pair instead of an object
can we just put a ref to key in the value? sure why not - err, bc we don't have access to it
+ solves const problem bc value is totally mutable by design
+ we can have multiple keys - and the value can have multiple refs to them
+ simpler equal and hash functions
map:
create an object with internal key(s)
create map index(es) with duplicate key values outside the object - dupe data is the downside
use set(s) with one static key for find():
create an object with internal key(s)
create set index(es) with specific hash/equals functor(s)
when finding, use one static key object (even across indexes!) so there isn't a big construction each time; just set the necessary key values
that proves difficult when dealing with member vars that are references
but to solve it, just set up a structure of dummy static key objects that use each other; then provide a function to setKey(Object& keyref) { keyref_ = keyref; }
nope, can't reassign refs
the solution: use pointers not references
yes that's right
just do it
apparently there was a reason i was anti-reference for all those years
two reasons to use pointers:
dynamically allocated
reassignment required
there ya go. simple. get it done.
when accessing find results from the set, use a const_cast on the object!
WARNING: a separate base class with the key sounds good... but fails when you have more than one index on the object. just use a static key object for them all!
|
c++11 example for large groups of objects with frequent crud AND search
|
Best solution is an unordered set of pointers:
typedef boost::unordered_set<MajorObject*> MajorObjects;
|
c++11 example for large groups of objects with infrequent crud and frequent search
|
Best solution is a vector of pointers sorted on demand (sorted_vector):
TODO
|
c++11 example to associate two complex objects (one the map key, one the map value)
|
Use unordered_map with a custom object as key. You must add hash and equals functions. Boost makes it easy:
static bool operator==(MyKeyObject const& m1, MyKeyObject const& m2)
{
return
m1.id_0 == m2.id_0
&& m1.id_1 == m2.id_1;
}
static std::size_t hash_value(MyKeyObject const& mko)
{
std::size_t seed = 0;
boost::hash_combine(seed, mko.id_0);
boost::hash_combine(seed, mko.id_1);
return seed;
}
typedef boost::unordered_map<MyKeyObject, MyValueObject*> MyMap;
Note that you can extend this to use a pointer to a key object, whoop.
|
c++11 example for multiple unordered_set indexes into one group of objects
|
Objects will be dynamically created. One set should include them all and be responsible for memory allocation cleanup:
TODO
|
c++11 example for set with specific sorting
|
Use set with a specific sort functor. You can create as many of these indexes as you want!
struct customers_set_sort_functor
{
bool operator()(const MyObject* l, const MyObject* r) const
{
// the id is the key
return l->id_ < r->id_;
}
};
typedef set<MyObject*,myobject_sort_by_id_functor> MyObjectsById;
|
c++11 loop through vector to erase some items
|
Note that other containers' iterators may not be invalidated so you can just erase() as needed...
For vectors, you have to play with iterators to get it right - watch for proper ++ pre/postfix!
for (it = numbers.begin(); it != numbers.end(); ) // NOTE we increment below, only if we don't erase
{
if (*it.no_good())
{
numbers.erase(it++); // NOTE that we ERASE THEN INCREMENT here.
}
else
{
++it;
}
}
I thought I had always looped backwards to do this, I *think* that's ok too, but I don't see it used in my code, I think I'll avoid. :-)
|
c++11 range based for loop, jacked with boost index if needed
|
No iterator usage at all. Nice at times, not enough at others. Make SURE to always use a reference or you will be working on a COPY. Make it const if you aren't changing the object.
for (auto& mc : my_container)
mc.setString("default");
for (const auto& cmc : my_container)
cout << cmc.asString();
boost index can give you the index if you need it, sweet:
#include <boost/range/adaptor/indexed.hpp>
...
for (const auto &element: boost::adaptors::index(mah_container))
cout << element.value() << element.index();
|
c++11 for loop using lambda
|
This C++11 for loop is clean and elegant and a perfect way to check if your compiler is ready for c++11:
vector<int> v;
for_each( v.begin(), v.end(), [] (int val)
{
cout << val;
} );
This is using a lambda function, we should switch from iterators and functors to those - but not quite yet, since we're writing cross-platform code. Do not touch this until we can be sure that all platforms provide compatible C++11 handling.
|
c++11 integer types
|
I really like the "fast" C++11 types, that give best performance for a guaranteed minimum bit width.
Use them when you know a variable will not exceed the maximum value of that bit width, but does not have to be a precise bit width in memory or elsewhere.
Pick specific-width fields whenever data is shared with other processes and components and you want a guarantee of its bit width.
And when using pointer size and array indices you should use types defined for those specific situations.
FAST types:
int_fast8_t
int_fast16_t fastest signed integer type with width of
int_fast32_t at least 8, 16, 32 and 64 bits respectively
int_fast64_t
uint_fast8_t
uint_fast16_t fastest unsigned integer type with width of
uint_fast32_t at least 8, 16, 32 and 64 bits respectively
uint_fast64_t
SMALL types:
int_least8_t
int_least16_t smallest signed integer type with width of
int_least32_t at least 8, 16, 32 and 64 bits respectively
int_least64_t
uint_least8_t
uint_least16_t smallest unsigned integer type with width of
uint_least32_t at least 8, 16, 32 and 64 bits respectively
uint_least64_t
EXACT types:
int8_t signed integer type with width of
int16_t exactly 8, 16, 32 and 64 bits respectively
int32_t with no padding bits and using 2's complement for negative values
int64_t (provided only if the implementation directly supports the type)
uint8_t unsigned integer type with width of
uint16_t exactly 8, 16, 32 and 64 bits respectively
uint32_t (provided only if the implementation directly supports the type)
uint64_t
SPECIFIC-USE types:
intptr_t integer type capable of holding a pointer
uintptr_t unsigned integer type capable of holding a pointer
size_t unsigned integer type capable of holding an array index (same size as uintptr_t)
|
C++11 scoped enumeration
|
C++11 has scoped enumeration, which lets you specify the SPECIFIC VARIABLE TYPE for the enum. Perfect, let's use uint_fast32_t.
enum class STRING_PREF_INDEX int_fast32_t: { ... };
Unfortunately, gcc gives me a scary warning, and stuff fails. For some reason, it does not know about the provided type, although it is definitely defined. Revisit this later if you have time.
warning: elaborated-type-specifier for a scoped enum must not use the ‘class’ keyword
Old skool is still cool:
typedef enum
{
// assert( SP_COUNT == 2 );
SP_FIRST = 0 ,
SP_SOME_PREF = SP_FIRST ,
SP_ANOTHA ,
SP_COUNT
} STRING_PREF_INDEX;
|
|