Passing a function generically to c++ template - c++

template<typename T, typename F>
auto mapV(vector<T> v, F f) {
vector<T> ret = {};
for(int i=0; i < v.size(); i++) {
ret[i] = f(v[i]);
}
return ret;
}
int main () {
vector<int> v = {1,2,3,4,5,6};
vector<int> b = mapV(v, [](int &t){ return t*2; });
for (int n : b) {
cout << "is " << n << endl;
}
return 0;
}
this code compiles but when run generates a segfault. What is the correct way to accept a function generically using templates? I would like to define the structure of the function also, being able to say that I only accept a function that takes a T and returns a T, but I'm not quite sure how to express that.
I tried with std::function<T(T)> but that didn't work either

vector<T> ret = {};
This creates an empty vector.
for(int i=0; i < v.size(); i++) {
ret[i] = f(v[i]);
This assigns something to the values in the vector ret. There are no existing values in the vector ret. There is no ret[0]. There is no ret[1], and so on. This is undefined behavior. operator[] does not add new values to the vector, it only modifies existing ones.
Replace this with the push_back() method.

Related

Swap array elements depending on function (given as argument) return

I have a problem with function that takes: array, size of that array and function (or lambda), and then sort given array depending of what function given in argument returns. For example:
int a1[] = {1,2,3,4,5,6};
part(a1,6,isEven); // where isEven is simple function for checking if element is even
Should return:
[ 2 4 6 1 5 3 ]
I already write this like that, where im retur new, sorted array:
template <typename T, typename FUN>
size_t part(T* arr, size_t size, FUN f) {
T new_arr[size] = {};
int first = 0;
int last = size - 1;
int index = 0;
bool changed = false;
for(int i = 0; i < size; i++){
if(f(arr[i])){
new_arr[first] = arr[i];
first++;
} else {
if(!changed){index = i;}
new_arr[last] = arr[i];
last--;
}
}
for(int j = 0; j < size; j++){
std::cout << new_arr[j] << " ";
}
return new_arr;
}
But I have to do this without using any other array. And it has to be done in one loop.
You were nearly there.
You must not use an additional array. But in line T new_arr[size] = {}; you try to do this. And, this will not work, because this is a VLA (Variable Lenth Array). VLAs are not part of the C++ language. Some compilers accept VLAs as extension, but, if you tell them to compile C++, then also those will reject it. In C++ the size of an array must be known at compile time. It must be a constant.
So, since you anyway work with pointers, you can also use new to allocate temporary memory. Then simply replace the above line with T* new_arr = new T[size]{};
And, you must not forget to delete the allocated memory. Or, you could copy the new data at the end of the function to the original and then delete the temporaray memory there.
So, in my opinion not the best approach. Anyway, the C++ standard library has the functions partition and stable_partition.
Anyway. Please see below:
#include <iostream>
#include <algorithm>
template <typename T, typename FUN>
T* part(T* arr, size_t size, FUN f) {
T* new_arr = new T[size]{};
int first = 0;
int last = size - 1;
int index = 0;
bool changed = false;
for (int i = 0; i < size; i++) {
if (f(arr[i])) {
new_arr[first] = arr[i];
first++;
}
else {
if (!changed) { index = i; }
new_arr[last] = arr[i];
last--;
}
}
return new_arr;
}
int main() {
int data[]{ 1,2,3,4,5,6 };
int *p = part(data, (sizeof(data) / sizeof(data[0])), [](const int i) {return i % 2 == 0; });
for (int i = 0; i < (sizeof(data) / sizeof(data[0])); ++i)
std::cout << p[i] << ' ';
delete[]p;
}

Template for different data types with similar properties

I'm trying to find out if there is any possibility in c++ to have a template for different custom data types with similar properties.
An example: Imagine two data types matrix2x2 and matrix3x3. Both data types consist of a standard array with 2 or 3 elements. Now I want to write a print function which takes a matrix-argument (either matrix2x2 or matrix3x3) and prints the contents depending on how long the internal array is.
Is there a possibility to do this, without writing the print function twice?
I have been searching on the internet a lot about this but I didnt find any information. It might be a duplicate question but I had a hard time looking for answers.
The class could looks like the following, with a member function to print the matrix:
template<typename T, std::size_t N, std::size_t M = N>
class Matrix
{
public:
Matrix()
{
// handle filling here
}
void print() const
{
for(int i = 0 ; i < N;i++) {
for(int j = 0; j < M;j++) {
std::cout << data[i][j] << " ";
}
std::cout << std::endl;
}
}
private:
T data[N][M];
};
Or also have a function as follow:
template<typename T, std::size_T N, std::size_T M = N>
void print(const Matrix<T,N,M>& mat)
{
// print ...
}
If you're allowed to make changes to the matrix class, see Vuwox's answer https://stackoverflow.com/a/56419728/7340043. If not, you can use this:
template<typename Matrix>
void printMatrix(const Matrix& m, std::ostream& out = std::cout)
{
for (size_t i = 0; i < m.height(); i++) {
for (size_t j = 0; j < m.width(); j++) {
out << m[i][j];
}
out << std::endl;
}
}

Flatten n-dimensional vector

I worked out an algorithm that can be used recursively, which works in a non-recursive manner, however, I cannot test it in recursive form because I cannot create a n-1 dimensional variable from the template.For help I write down the variable names and its meaning:
v: the n-dimensional vector
dims: n long vector with every dimension (e.g: if it was an array looking like int x[3][6][4], then dims looks like: {3, 6, 4})
p: product of the dimensions, used to get the size of the flat vector (e.g: 3*6*4)
ret: the returned flat vector
sub_dims: same as dims execpt without the first dimension (e.g: {6, 4})
sub_p: same as p except without the first factor (e.g: 6*4)
sub_ret: the returned flat vector for the n-1 dimensional vector
the code:
template <typename T>
vector<int> dim_flat(vector<T> v, vector<int> dims)
{
// creating variables and vectors
int n = dims.size();
int p = dims[0];
for (int i = 1; i < n; i++)
{
p *= dims[i];
}
int sub_p = p / dims[0];
vector<int> sub_dims;
sub_dims.assign(dims.begin() + 1, dims.end());
vector<int> ret(p);
// algorithm
if (n > 1)
{
for (int i = 0; i < dims[0]; i++)
{
vector<int> sub_ret = dim_flat(v[i], sub_dims);
for (int j = 0; j < sub_p; j++)
{
ret[i * sub_p + j] = sub_ret[j];
}
}
}
else
{
// case for the input is 1D
// not yet written
}
return ret;
}
With this code the project build, however if I call it in main:
vector<int> ret = dim_flat(v, dims);
where v is e.g. an 4D vector and dims is a vector containing {3, 3, 3, 3}, then I get the following when trying to build:
error C2784: 'std::vector<_Ty> dim_flat(std::vector<T>,std::vector<_Ty>)' : could not deduce template argument for 'std::vector<T>' from 'int'
for the line
vector<int> sub_ret = dim_flat(v[i], sub_dims);
I kinda (but not really) understand the meaning of this error, so I expected the same to happen, which it did for this:
T x = v[i];
vector<int> sub_ret = dim_flat(x, sub_dims);
This is the part where I don't really understand the error anymore, because I thought that with the argument vector<T> v I specified that the input e.g. 4D vector will be understood as a vector of T where T is a 3D vector that can be also indexed since its a vector<T>. So following this logic I thought that if I give the recursion the first T being a 3D vector, then a step deeper this 3D vector will now be understood as a vector of T' where T' is a 2D vector, and so on.
Clearly either my logic is flawed, or I used the wrong method (, or both), so the question is: How can I solve / fix this?
EDIT:Credit to Max66 for the solution.
The code probably could be more optimized, but at least now it works.
Code:
//// for integer only
// case input is 0-dimensional (simply a variable)
template <typename T>
vector<int> dim_flat (const T &v, const vector<int> &dims)
{
return vector<int>(1, v);
}
// case input is n-dimensional
template <typename T>
vector<int> dim_flat(const vector<T> &v, const vector<int> &dims)
{
// creating variables and vectors
int n = dims.size();
int p = dims[0];
for (int i = 1; i < n; i++)
{
p *= dims[i];
}
int sub_p = p / dims[0];
vector<int> sub_dims;
sub_dims.assign(dims.begin() + 1, dims.end());
vector<int> ret(p);
// algorithm
if (n > 1) // case n-dimensional
{
for (int i = 0; i < dims[0]; i++)
{
vector<int> sub_ret = dim_flat(v[i], sub_dims);
for (int j = 0; j < sub_p; j++)
{
ret[i * sub_p + j] = sub_ret[j];
}
}
}
else // case 1-dimensional
{
for (int i = 0; i < p; i++)
{
vector<int> sub_ret = dim_flat(v[i], sub_dims);
ret[i] = sub_ret[0];
}
}
return ret;
}
Short answer: add the function
std::vector<int> dim_flat (int v, std::vector<int> const &)
{ return {v}; }
if you can use C++11 or newer, or
std::vector<int> dim_flat (int v, std::vector<int> const &)
{ return std::vector<int>(1, v); }
if you have to use C++98
Long answer: if I'm not wrong, the problem is that, when you call dim_flat() with an std::vector<int> (when T is int), you have n == 1, so dim_flat() isn't called (is executed the "case for the input 1D") but the compiler isn't so smart to understand that there is no need to call dim_flat() with int (instead std::vector<T>) so look for dim_flat(int, std::vector<int>) and doesn't find it.
So, to make the compiler happy, you have to implement dim_flat(int, std::vector<int>).
Can be a dummy function (that return an empty vector), but I suggest you to implement it correctly.
A little of topic: please, avoid unusefull copies of vectors; in your dim_flat(), v and dims are only readed, not modified; so you can receive they as const reference, as follows
template <typename T>
std::vector<int> dim_flat(std::vector<T> const & v,
std::vector<int> const & dims)
P.s.: why don't you simply write as follows ?
std::vector<int> dim_flat (std::vector<int> const & v)
{ return v; }
template <typename T>
std::vector<int> dim_flat(std::vector<std::vector<T>> const & v)
{
std::vector<int> ret;
for ( auto const & e : v )
{
auto s = dim_flat(e);
ret.reserve( ret.size() + s.size() );
ret.insert( ret.end(), s.cbegin(), s.cend() );
}
return ret;
}

How to sort and rank a vector in C++ (without using C++11)

I am trying to construct a function take takes a vector, ranks it, sorts it and outputs the sorted and ranked vector with the original positioning of the values. For example: Input: [10,332,42,0.9,0] Output: [3, 5, 4, 2, 1]
I used this stack overflow question (specifically Marius' answer) as a reference guide, however I am stuck with my code now and do not understand where the issue is.
I am running a C++03.
One of the errors I get is
error: invalid types ‘const float*[float]’ for array subscript’ for array subscript on my if statement.
//Rank the values in a vector
std::vector<float> rankSort(const float *v_temp, size_t size)
{
vector <float> v_sort;
//create a new array with increasing values from 0 to n-1
for(unsigned i = 0; i < size; i++)
{
v_sort.push_back(i);
}
bool swapped = false;
do
{
for(unsigned i = 0; i < size; i++)
{
if(v_temp[v_sort[i]] > v_temp[v_sort[i+1]]) //error line
{
float temp = v_sort[i];
v_sort[i] = v_sort[i+1];
v_sort[i+1] = temp;
swapped = true;
}
}
}
while(swapped);
return v_sort;
}
std::vector<float> rankSort(const std::vector<float> &v_temp)
{
return rankSort(&v_temp[0], v_temp.size());
}
Your problem is a misconception on rankings. Array indices are of size_t not float, so you'll need to return a vector<size_t> not a vector<float>.
That said your sort is O(n2). If you're willing to use more memory we can get that time down to O(n log(n)):
vector<size_t> rankSort(const float* v_temp, const size_t size) {
vector<pair<float, size_t> > v_sort(size);
for (size_t i = 0U; i < size; ++i) {
v_sort[i] = make_pair(v_temp[i], i);
}
sort(v_sort.begin(), v_sort.end());
pair<double, size_t> rank;
vector<size_t> result(size);
for (size_t i = 0U; i < size; ++i) {
if (v_sort[i].first != rank.first) {
rank = make_pair(v_sort[i].first, i);
}
result[v_sort[i].second] = rank.second;
}
return result;
}
Live Example
EDIT:
Yeah this actually gets a little simpler when taking a vector<float> instead of a float[]:
vector<size_t> rankSort(const vector<float>& v_temp) {
vector<pair<float, size_t> > v_sort(v_temp.size());
for (size_t i = 0U; i < v_sort.size(); ++i) {
v_sort[i] = make_pair(v_temp[i], i);
}
sort(v_sort.begin(), v_sort.end());
pair<double, size_t> rank;
vector<size_t> result(v_temp.size());
for (size_t i = 0U; i < v_sort.size(); ++i) {
if (v_sort[i].first != rank.first) {
rank = make_pair(v_sort[i].first, i);
}
result[v_sort[i].second] = rank.second;
}
return result;
}
Live Example
//Rank the values in a vector
std::vector<size_t> rankSort(const std::vector<float> &v_temp)
{
vector <size_t> v_sort;
//create a new array with increasing values from 0 to size-1
for(size_t i = 0; i < v_temp.size(); i++)
v_sort.push_back(i);
bool swapped = false;
do
{
swapped = false; //it's important to reset swapped
for(size_t i = 0; i < v_temp.size()-1; i++) // size-2 should be the last, since it is compared to next element (size-1)
if(v_temp[v_sort[i]] > v_temp[v_sort[i+1]])
{
size_t temp = v_sort[i]; // we swap indexing array elements, not original array elements
v_sort[i] = v_sort[i+1];
v_sort[i+1] = temp;
swapped = true;
}
}
while(swapped);
return v_sort;
}
v_sort[i] is a float (it's just an element of v_sort vector) while only integral types can be used as array subscripts.
Probably you meant v_sort as an array of indices, thus, you should declare it as std::vector<size_t> or std::vector<int> something like that.
UP: Also, given that you change the values of the array passed, it's not an elegant way of pass it by const reference.
To sum up, the following code compiles correctly on my machine:
std::vector<unsigned> rankSort(float *v_temp, size_t size)
{
vector <unsigned> v_sort;
//create a new array with increasing values from 0 to n-1
for(unsigned i = 0; i < size; i++)
{
v_sort.push_back(i);
}
bool swapped = false;
do
{
for(unsigned i = 0; i < size; i++)
{
if(v_temp[v_sort[i]] > v_temp[v_sort[i+1]]) //error line
{
unsigned temp = v_sort[i];
v_sort[i] = v_sort[i+1];
v_sort[i+1] = temp;
swapped = true;
}
}
}
while(swapped);
return v_sort;
}
std::vector<unsigned> rankSort(std::vector<float> &v_temp)
{
return rankSort(&v_temp[0], v_temp.size());
}
I suggest you adopt a more robust solution by taking advantage of what you have in the STL. To do so, we will first make an "index vector", ie. a std::vector<std::size_t> vsuch that for any i, v[i] == i is true:
// I'm sure there's a more elegant solution to generate this vector
// But this will do
std::vector<std::size_t> make_index_vector(std::size_t n) {
std::vector<std::size_t> result(n, 0);
for (std::size_t i = 0; i < n; ++i) {
result[i] = i;
}
return result;
}
Now all we have to do is to sort this vector according to a specific comparison function that will use the input vector. Furthermore, to allow for the most generic approach we will give the user the opportunity to use any comparison functor:
template <typename T, typename A, typename Cmp>
struct idx_compare {
std::vector<T, A> const& v;
Cmp& cmp;
idx_compare(std::vector<T, A> const& vec, Cmp& comp) : v(vec), cmp(comp) {}
bool operator()(std::size_t i, std::size_t j) {
return cmp(v[i], v[j]);
}
};
template <typename T, typename A, typename Cmp>
std::vector<std::size_t> sorted_index_vector(std::vector<T, A> const& vec, Cmp comp) {
std::vector<std::size_t> index = make_index_vector(vec.size());
std::sort(index.begin(), index.end(),
idx_compare<T, A, Cmp>(vec, comp));
return index;
}
In the sorted index vector, index[0] is the index of the lowest value in the input vector, index[1] the second lowest and so on. Therefore, we need one additional step to get the rank vector from this one:
std::vector<std::size_t> get_rank_vector(std::vector<std::size_t> const& index) {
std::vector<std::size_t> rank(index.size());
for (std::size_t i = 0; i < index.size(); ++i) {
// We add 1 since you want your rank to start at 1 instead of 0
// Just remove it if you want 0-based ranks
rank[index[i]] = i + 1;
}
return rank;
}
Now we combine all the pieces together:
template <typename T, typename A, typename Cmp>
std::vector<std::size_t> make_rank_vector(
std::vector<T, A> const& vec, Cmp comp) {
return get_rank_vector(sorted_index_vector(vec, comp));
}
// I had to stop using default template parameters since early gcc version did not support it (4.3.6)
// So I simply made another overload to handle the basic usage.
template <typename T, typename A>
std::vector<std::size_t> make_rank_vector(
std::vector<T, A> const& vec) {
return make_rank_vector(vec, std::less<T>());
}
Result with [10, 332, 42, 0.9, 0]: [3, 5, 4, 2, 1].
You can find a Live Demo on gcc 4.3.6 to explicit this behavior.
Here is my codes using STL to achieve this in a concise way to get the rank.
template <typename T>
vector<size_t> calRank(const vector<T> & var) {
vector<size_t> result(var.size(),0);
//sorted index
vector<size_t> indx(var.size());
iota(indx.begin(),indx.end(),0);
sort(indx.begin(),indx.end(),[&var](int i1, int i2){return var[i1]<var[i2];});
//return ranking
for(size_t iter=0;iter<var.size();++iter){
result[indx[iter]]=iter+1;
}
return result;
}

Generic method for flattening 2d vectors

I have a function to flatten out an vector of vectors to a single vector. Comming from c# i would write it something like:
vector<T> flatten(vector<vector<T>> 2dVector)
{
vector<T> newVector(2dVector.size()*2dVector[0].size())
for (int i = 0; i < 2dVector.size(); i++)
{
for (int j = 0; j < 2dVector[i].size(); j++)
{
newVector[j + i * 2dVector.size()] = 2dVector[i][j];
}
}
return newVector;
}
but this code gives 20+ errors in msvc++
After hours of seaching the web for how to make this function, i modified the method signature to
utilities.h:
template <typename A, typename B> A flatten(const B 2dVector&);
utilities.cpp:
template <typename A, typename B> A flatten(const B 2dVector&)
{
A newVector(2dVector.size()*2dVector[0].size())
for (int i = 0; i < 2dVector.size(); i++)
{
for (int j = 0; j < 2dVector[i].size(); j++)
{
newVector[j + i * 2dVector.size()] = 2dVector[i][j];
}
}
return newVector;
}
But i still get on the order of ~15 errors from this code, and i am all out of ideas. Any suggestions?
Your code contains several problems. To name a few:
An identifier cannot start with a number.
Your template should be parameterized by a single parameter - the basic value type of the returned vector
Your code internally assumes that the vectors are same-sized, where it is just as easy to accommodate a ragged array
There are more efficient ways to append a vector to the end of a vector.
I'd suggest the following alternative:
#include <vector>
template<typename T>
std::vector<T> flatten(const std::vector<std::vector<T>> &orig)
{
std::vector<T> ret;
for(const auto &v: orig)
ret.insert(ret.end(), v.begin(), v.end());
return ret;
}
int main()
{
std::vector<std::vector<int>> vv;
vv.push_back(std::vector<int>{1, 2, 3});
vv.push_back(std::vector<int>{10, 20});
flatten(vv);
}