C++ class specialiation when dealing with STL containers - c++

I'd like a function to return the size in bytes of an object for fundamental types. I'd also like it to return the total size in bytes of an STL container. (I know this is not necessarily the size of the object in memory, and that's okay).
To this end, I've coded a memorysize namespace with a bytes function such that memorysize::bytes(double x) = 8 (on most compilers).
I've specialized it to correctly handle std::vector<double> types, but I don't want to code a different function for each class of the form std::vector<ANYTHING>, so how do I change the template to correctly handle this case?
Here's the working code:
#include <iostream>
#include <vector>
// return the size of bytes of an object (sort of...)
namespace memorysize
{
/// general object
template <class T>
size_t bytes(const T & object)
{
return sizeof(T);
}
/// specialization for a vector of doubles
template <>
size_t bytes<std::vector<double> >(const std::vector<double> & object)
{
return sizeof(std::vector<double>) + object.capacity() * bytes(object[0]);
}
/// specialization for a vector of anything???
}
int main(int argc, char ** argv)
{
// make sure it works for general objects
double x = 1.;
std::cout << "double x\n";
std::cout << "bytes(x) = " << memorysize::bytes(x) << "\n\n";
int y = 1;
std::cout << "int y\n";
std::cout << "bytes(y) = " << memorysize::bytes(y) << "\n\n";
// make sure it works for vectors of doubles
std::vector<double> doubleVec(10, 1.);
std::cout << "std::vector<double> doubleVec(10, 1.)\n";
std::cout << "bytes(doubleVec) = " << memorysize::bytes(doubleVec) << "\n\n";
// would like a new definition to make this work as expected
std::vector<int> intVec(10, 1);
std::cout << "std::vector<int> intVec(10, 1)\n";
std::cout << "bytes(intVec) = " << memorysize::bytes(intVec) << "\n\n";
return 0;
}
How do I change the template specification to allow for the more general std::vector<ANYTHING> case?
Thanks!

Modified your code accordingly:
/// specialization for a vector of anything
template < typename Anything >
size_t bytes(const std::vector< Anything > & object)
{
return sizeof(std::vector< Anything >) + object.capacity() * bytes( object[0] );
}
Note that now you have a problem if invoking bytes with an empty vector.
Edit: Scratch that. If I remember your previous question correctly, then if you get a vector of strings then you would like to take into account the size taken by each string. So instead you should do
/// specialization for a vector of anything
template < typename Anything >
size_t bytes(const std::vector< Anything > & object)
{
size_t result = sizeof(std::vector< Anything >);
foreach elem in object
result += bytes( elem );
result += ( object.capacity() - object.size() ) * sizeof( Anything ).
return result;
}

Related

Returning unique_ptr from std::for_each + lambda functions

I am facing a problem with returning std::moved unique_pointers to a lambda. Once I have moved the pointer to the lambda function how do I take the ownership back from the lambda?
In the following code I am demonstrating my problem. I have taken a snipped out from code-base and moved everything to main to explain the problem better. First question is marked as "QUESTION 1" - where I want to understand if I am correct in using (*v) for accessing my vector.
The code creates a vector of few numbers, and then iterates over the vector to mark the bits in the bitmap. I think the bits are marked correctly as I am able to print them in the lambda itself. After marking the bits, I want the ownership back. How do I do it? I need to return the bitmap pointer to the caller function.
How to take the ownership back in a STANDARD way from a lambda rather than hacking around the passed unique_ptr or avoiding moving the pointer to lambda. Does cpp17 support this?
Compile the code with g++ -std=c++17
#include <memory>
#include <algorithm>
#include <iostream>
#include <vector>
int main () {
int increments = 10;
int numberOfElements = 10;
/* Create the vector */
auto v = std::make_unique<std::vector<int>>(std::vector<int> (numberOfElements));
/* QUESTION 1 - is (*v) the right way to access it or there is a better way. */
std::generate((*v).begin(), (*v).end(), [n=0, increments]() mutable { n = n + increments; return n;});
/* Print the generated elements */
std::cout << "\nPrinting the generated elements ";
std::for_each((*v).begin(), (*v).end(), [](int n) { std::cout<<" " << n;});
/* Int find the maximum element */
int maxElement = *(std::max_element((*v).begin(), (*v).end()));
/* Making a bitmap of the elements */
std::cout << "\nPrinting the maxElement " << maxElement;
auto bitmap = std::make_unique<std::vector <bool>> (std::vector<bool>(maxElement + 1));
/* Now setting all the elements in the vector to true in the bitmap. */
for_each((*v).begin(), (*v).end(), [bmap = std::move(bitmap)](int n) {
(*bmap).at(n) = true;
if ((*bmap).at(n) == true) {
std::cout << "\nBit "<< n <<" marked";
}
});
/*******************************************************
* Question 2 : I now need the ownership of bitmap back.
* How to do it ?. Bitmap is now null after moving it to the lambda.
*/
if (bitmap) {
std::cout << "\nafter reset, ptr is not NULL ";
} else if (bitmap == nullptr) {
std::cout << "\nbitmap is null";
}
}
QUESTION 1 - is (*v) the right way to access it or there is a better way.
Alternative is using ->, so v->begin() instead of (*v).begin().
but it is strange to use unique_ptr for vector.
You might simply do:
std::vector<int> v(numberOfElements);
std::generate(v.begin(), v.end(),
[n=0, increments]() mutable { n = n + increments; return n;});
std::cout << "\nPrinting the generated elements ";
for (int e : v) { std::cout << " " << e; };
int maxElement = *std::max_element(v.begin(), v.end());
// ...
Question 2 : I now need the ownership of bitmap back. How to do it ?
You cannot do it with lambda, in your case capturing by reference do the job (even if the lambda would own the vector, the for_each "block" doesn't):
std::vector<bool> bitmap(maxElement + 1);
for_each(v.begin(), v.end(), [&bmap](int n) {
bmap.at(n) = true;
std::cout << "\nBit "<< n <<" marked";
});
assert(!bitmap.empty());
if (bitmap.empty()) {
std::cout << "\nbitmap is empty";
} else if (bitmap == nullptr) {
std::cout << "\nbitmap is NOT empty";
}
If you replace your lambda with your own functor, you might do something like
class MyMarker
{
public:
MyMarker(std::vector<bool>&& bitmap) : bitmap(std::move(bitmap)) {}
MyMarker(const MyMarker&) = delete;
MyMarker& operator =(const MyMarker&) = delete;
void operator() (int n) const
{
bitmap.at(n) = true;
std::cout << "\nBit "<< n <<" marked";
}
std::vector<bool> TakeBack() { return std::move(bitmap); }
private:
std::vector<bool> bitmap;
};
and then:
std::vector<bool> bitmap(maxElement + 1);
MyMarker myMarker(std::move(bitmap));
assert(bitmap.empty());
std::for_each(v.begin(), v.end(), myMarker);
bitmap = myMarker.TakeBack();
assert(!bitmap.empty());

Map reference confusion

I encountered with some weird problem. I have class which store its values inside map. But in one case I need to expose map to do some external calculation and possible adding of data inside that map.
And I have next problem. I have shared_ptr of that class and expose map through reference, but during processing map wont accept new data.
I wrote some dummy example of that just to be clear. What is happening here? And why?
Why changes made to map won't hold up after function end?
#include <map>
#include <iostream>
#include <memory>
class MapWrap {
public:
MapWrap() {}
~MapWrap(){}
std::map<int, int>& getMap() { return map; }
private:
std::map<int, int> map;
};
void goGo(std::shared_ptr<MapWrap> m){
auto map = m->getMap();
std::cout << "Func: before: map size: " << map.size() << std::endl;
for(int i = 0; i < 3; ++i){
// This should and will add new value to map.
if(map[i] == 3){
std::cout << "blah" << std::endl;
}
}
std::cout << "Func: after: map size: " << map.size() << std::endl;
}
int main(){
auto mapWrap = std::make_shared<MapWrap>();
for(int i = 0; i < 3; ++i){
goGo(mapWrap);
}
return 0;
}
EDIT: Removed const from getMap() method.
The problem is that here:
auto map = m->getMap();
type of map is std::map<int, int> so you make a copy and you modify this copy. Change it to :
auto& map = m->getMap();
and you will modify the passed map instead of copy.
btw. if you dont know what type your auto variable have, you can always use compiler errors to check this:
template<typename T> struct TD;
auto map = m->getMap();
TD<decltype(map)> dd;
will result in:
main.cpp:19:21: error: aggregate 'TD<std::map<int, int> > dd' has incomplete type and cannot be defined
TD<decltype(map)> dd;
here you can read map type is std::map<int, int>

Static variables and functions that are called once for each choice of arguments

Here is a simple C++ question.
Description of the problem:
I have a function that takes as input an integer and returns a vector of zeros with length the input. Assume that I call the function many times with the same argument. What I want to avoid is that my function creates the vector of zeroes each time it is called. I want this to happen only the first time the function is called with the given input.
How I approached it: This brought to mind static variables. I thought of creating a static vector that holds the required zero vectors of each size, but wasn't able to figure out how to implement this. As an example I want something that "looks" like [ [0], [0,0], ...].
If there is a different way to approach such a problem please feel free to share! Also, my example with vectors is a bit specialised but replies that are more generic (concerning static variables that depend on the argument) would be greatly appreciated.
Side question:
To generalise further, is it possible to define a function that is only called once for each choice of arguments?
Thanks a lot.
You can have a map of sizes and vectors, one vector for each size:
#include <vector>
#include <map>
#include <cstddef>
std::vector<int>& get_vector(std::size_t size)
{
static std::map<size_t, std::vector<int> > vectors;
std::map<size_t, std::vector<int> >::iterator iter = vectors.find(size);
if (iter == vectors.end())
{
iter = vectors.insert(std::make_pair(size, std::vector<int>(size, 0))).first;
}
return iter->second;
}
If I understand correctly what you are trying to do, I don't think you will get the benefit you are expecting.
I wrote a quick benchmark to compare the performance of repeatedly creating a vector of zeros. The first benchmark uses the standard vector constructor. The second uses a function that only creates the vector the first time and stores it in a map:
const std::vector<int>& zeros(std::size_t size) {
static std::unordered_map<size_t, std::vector<int>> vectors;
auto find = vectors.find(size);
if (find != vectors.end())
return find->second;
auto insert = vectors.emplace(size, std::vector<int>(size));
return insert.first->second;
}
std::chrono::duration<float> benchmarkUsingMap() {
int sum = 0;
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i != 10'000; ++i) {
auto zeros10k = zeros(10'000);
zeros10k[5342] = 1;
sum += zeros10k[5342];
}
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Sum: " << sum << "\n";
return end - start;
}
std::chrono::duration<float> benchmarkWithoutUsingMap() {
int sum = 0;
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i != 10'000; ++i) {
auto zeros10k = std::vector<int>(10'000);
zeros10k[5342] = 1;
sum += zeros10k[5342];
}
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Sum: " << sum << "\n";
return end - start;
}
int main() {
std::cout << "Benchmark without map: " << benchmarkWithoutUsingMap().count() << '\n';
std::cout << "Benchmark using map: " << benchmarkUsingMap().count() << '\n';
}
Output:
Benchmark without map: 0.0188374
Benchmark using map: 0.134966
So, in this case, just creating the vector each time was almost 10x faster. This is assuming you want to create a mutable copy of the vector of zeros.
If each vector needs to be a separate instance then you will have to have a construction for each instance. Since you will have to construct each instance you can make a simple make_int_vector function like:
std::vector<int> make_int_vector(std::size_t size, int fill = 0)
{
return std::vector(size, fill);
}
The returned vector will either be moved or be elided with copy elision
What you are asking for is a cache. The hard part is how long an entry should exist in the cache. Your current requirement seems to be an eternal cache, meaning that each entry will persist for ever. For such a simple use case, à static map is enough:
template<typename T, typename U>
T cached(T (*funct)(U arg)) {
static unordered_map<U, T> c;
if (c.count(arg) == 0) {
c[arg] = funct(arg);
}
return c[arg];
}
The above is returning a value,which will require à copy. If you want to avoid the copy, just return a reference, but then, if you change one of the vectors, the next call will return the modified value.
template<typename T, typename U>
&T cached(T (*funct)(U arg)) {
static unordered_map<U, T> c;
if (c.count(arg) == 0) {
c[arg] = funct(arg);
}
return c[arg];
}

resizing boost::multi_array to match another

I need to resize one multi_array to the size of another.
In Blitz++ I could just do
arr1.resize(arr2.shape());
Is there a multi_array solution of similar length? Because
arr1.resize(boost::extents[arr2.shape()[0]][arr2.shape()[1]]);
seems a little long and arduous.
You can use the shape() member. Sadly it cannot directly serve as an ExtentList (it doesn't model the Collection concept) but it's easy to make it into one:
using MA = multi_array<double, 2>;
MA ma(extents[12][34]);
auto& ma_shape = reinterpret_cast<boost::array<size_t, MA::dimensionality> const&>(*ma.shape());
So that
// demo
std::cout << "[" << ma_shape[0] << "][" << ma_shape[1] << "]\n";
Prints [12][34].
Now, ma_shape can directly be used to reshape/resize another array:
Live On Coliru
#include <boost/multi_array.hpp>
#include <iostream>
int main() {
using namespace boost;
using MA = multi_array<double, 2>;
MA ma(extents[12][34]);
auto& ma_shape = reinterpret_cast<boost::array<size_t, MA::dimensionality> const&>(*ma.shape());
// demo
std::cout << "[" << ma_shape[0] << "][" << ma_shape[1] << "]\n";
// resize
MA other;
assert(!std::equal(ma_shape.begin(), ma_shape.end(), other.shape()));
other.resize(ma_shape);
assert(std::equal(ma_shape.begin(), ma_shape.end(), other.shape()));
// reshape
other.resize(extents[1][12*34]);
assert(!std::equal(ma_shape.begin(), ma_shape.end(), other.shape()));
other.reshape(ma_shape);
assert(std::equal(ma_shape.begin(), ma_shape.end(), other.shape()));
}
I think this is another oversight of Boost.MultiArray. I have written a bunch of "utility" functions that allows to take the shape (dimension sizes), base indices (e.g. 0 or 1 for each dimension) and extensions (base indices and sizes in each dimension).
namespace boost{
template<class MultiArray>
detail::multi_array::extent_gen<MultiArray::dimensionality>
extension(MultiArray const& ma){ //this function is adapted from
typedef detail::multi_array::extent_gen<MultiArray::dimensionality> gen_type;
gen_type ret;
typedef typename gen_type::range range_type;
for(int i=0; i != MultiArray::dimensionality; ++i)
ret.ranges_[i] = range_type(ma.index_bases()[i], ma.index_bases()[i]+ma.shape()[i]);
return ret;
}
}
Which is later used as:
boost::multi::array<double, 3> m(boost::multi::extents[3][4][5]);
boost::multi::array<double, 3> n(extension(m)); // n takes the extension (and shape) of m
(If the base indexes are not zero it also works. It works also for view and other multi_array-type classes.)

Vector point to another vector

What I have here is two arrays of different types that I'm converting to vectors.
int ham_array[] = {32,71,12,45,26};
char word_array[] = {"cat", "bat", "green", "red", "taxi"};
vector < int > hamvector (ham_array, ham_array + 5);
vector < char > wordvector(word_array, word_array + 5);
I am going to call a sort function to sort the elements of ham_array from least to greatest. At the same time, I would like the word_array to also get sorted the same way ham_vector gets sorted using references.
For example,
after I call sort(hamvector)
ham_array[] = {12, 26, 32, 45, 71}
and sort(wordvector)
word_array[] = {"green", "taxi", "cat", "red", "bat"};
Is there an easy way to do this?
Well for one thing, that would be char *word_array[], the way you declared it would be a string.
Anyway the way to do this is you declare a structure to keep these things paired:
struct t {string name; int number;};
vector<t> list;
// fill in list
// comparer to compare two such structs
bool comparer(t &a, t &b) { return a.number>=b.number; }
// and to sort the list
sort(list.begin(), list.end(), comparer);
If by simple, you mean a more direct way then yes. The std::sort() does support sorting of raw arrays as well:
sort(word_array, word_array + 5, wordcmp);
As Blindy showed, you need a comparator function to tell sort how the ordering is suppose to be done for your list of words. Otherwise you'll end up sorting by the memory address that the string resides at instead of by the letters in your string. Something like this should work:
int wordcmp(const char *lhs, const char *rhs)
{
return strncmp(lhs, rhs, 256) < 0;
}
One other note, in practice you'll want to prefer std::vector over just raw pointer arrays since the latter isn't as safe.
I've tried to find a solution to a similar problem before and ultimately had to sort it manually. Another way I imagine you could do this would be to write a sorter functor that can somehow figure out, based on which string is being sorted, which integer is associated, and sort based on that. This is terribly inefficient, so I would highly advise doing your own manual sorting using std::swap.
#include <map>
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
template<typename KeyType, typename ValueType>
class CMappedSorter
{
std::map<KeyType, ValueType>* const m_Mappings;
public:
CMappedSorter(std::map<KeyType, ValueType>* Mappings) : m_Mappings(Mappings)
{
}
bool operator()(KeyType& LHS, KeyType& RHS)
{
const ValueType LHSSortingValue = m_Mappings->find(LHS)->second;
const ValueType RHSSortingValue = m_Mappings->find(RHS)->second;
return (LHSSortingValue < RHSSortingValue);
}
};
int main(int argc, char* argv[])
{
std::vector<int> Integers;
std::vector<std::string> Strings;
Integers.push_back(3);
Integers.push_back(1);
Integers.push_back(2);
Strings.push_back("Apple");
Strings.push_back("Banana");
Strings.push_back("Cherry");
std::map<std::string, int> Mappings;
if(Integers.size() == Strings.size())
{
const unsigned int ElementCount = Strings.size();
// Generate mappings.
auto StringsIterator = Strings.begin();
auto IntegersIterator = Integers.begin();
for(unsigned int i = 0; i < ElementCount; ++i)
{
Mappings[*(StringsIterator)] = *(IntegersIterator);
++StringsIterator;
++IntegersIterator;
}
// Print out before sorting.
std::cout << "Before Sorting" << std::endl;
std::cout << "Int\tString" << std::endl;
StringsIterator = Strings.begin();
IntegersIterator = Integers.begin();
for(unsigned int i = 0; i < ElementCount; ++i)
{
std::cout << *(IntegersIterator) << '\t' << *(StringsIterator) << std::endl;
++StringsIterator;
++IntegersIterator;
}
// Sort
std::sort(Strings.begin(), Strings.end(), CMappedSorter<std::string, int>(&(Mappings)));
std::sort(Integers.begin(), Integers.end());
// Print out after sorting.
std::cout << "After Sorting" << std::endl;
std::cout << "Int\tString" << std::endl;
StringsIterator = Strings.begin();
IntegersIterator = Integers.begin();
for(unsigned int i = 0; i < ElementCount; ++i)
{
std::cout << *(IntegersIterator) << '\t' << *(StringsIterator) << std::endl;
++StringsIterator;
++IntegersIterator;
}
}
else
{
std::cout << "Error: Number of elements in each container are not equivalent." << std::endl;
}
}