How to pass fields from a class to function in c++? - c++

In some words: how can I pass various fields from a custom class to a single function?
Now in details:
I have a std::vector containing a class, for example CustomClass from which I have to extract a result from a field from this class by some criteria which are fields in this class and to combine somehow this data.
My first approach to this problem was to use a function which accepts as a parameter the std::vector of the class in order to extract the data and return a std:map. The key in this map is the type of the criteria by which the data should be combined and the value is an int with the combined data from all members of this vector.
The problem is that the criteria is not only one - more than one field from this class may be used as criteria (let for easiness all of the criteria are std::string, if they are not - I could make the function templated).
The easiest way for me now is to make dozens of functions with almost identical code and each of them to extract a simple concrete field from this class. However changes might require similar changes to all of the dozens of functions which would be a maintenance headache. But in this stage I cannot think how to pass to a single function a field from this class...
Here's an example code from this class:
// this is the class with data and criteria
class CustomClass
{
public:
std::string criteria1;
std::string criteria2;
std::string criteria3;
//... and others criteria
int dataToBeCombined;
// other code
};
// this is one of these functions
std::map<std::string, int> getDataByCriteria1(std::vector<CustomClass> aVector)
{
std::map<std::string, int> result;
foreach(CustomClass anObject in aVector)
{
if(result.find(anObject.criteria1)==result.end()) // if such of key doesn't exists
{
result.insert(std::make_pair(anObject.criteria1, anObject.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
}
}
return result;
}
and by similar way I should make the other functions which should work with CustomClass::criteria2, CustomClass::criteria3, etc.
I thought to make these criteria in a single array and to pass to this function only the number of the criteria but the class will be used by others for other purposes and the fields must be easy to read, so this will not be an option (i.e. the real names are not criteria1, criteria2, etc. but are descriptive).
Anyone with ideas?
EDIT: Someone referred my question to "C++ same function parameters with different return type" which obviously is very different - the function in my case return the same type every time, just the parameters it takes must be various fields from a class.

You can use pointer to member. Declare an argument std::string CustomClass::*pField in your function, pass it with &CustomClass::criteriaN, access it with anObject.*pField.
See more on the topic: Pointers to data members.

If all "criteria" are of the same type, I don't see an elegant solution but you can "enumerate" they in some way and use their number.
By example, you can declare a templated getVal() method in CustomClass in this way
template <int I>
const std::string & getVal () const;
and implement they, number by number, criteria by criteria, in this way (outside the body of the class)
template <>
const std::string & CustomClass::getVal<1> () const
{ return criteria1; }
template <>
const std::string & CustomClass::getVal<2> () const
{ return criteria2; }
template <>
const std::string & CustomClass::getVal<3> () const
{ return criteria3; }
Now, you can transform getDataByCriteria1() in a templated function getDataByCriteria() in this way
template <int I>
std::map<std::string, int> getDataByCriteria (std::vector<CustomClass> aVector)
{
std::map<std::string, int> result;
for (const auto & cc : aVector)
{
if ( result.find(cc.getVal<I>()) == result.end()) // if such of key doesn't exists
{
result.insert(std::make_pair(cc.getVal<I>(), cc.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
}
}
return result;
}
and call it in this way
auto map1 = getDataByCriteria<1>(ccVec);
auto map2 = getDataByCriteria<2>(ccVec);
auto map3 = getDataByCriteria<3>(ccVec);
--- EDIT: added solution (C++14 only) for different types criteria ---
A little different if the "criteria" are of different types.
The solution work but in C++14, thanks to auto and decltype().
By example, if
std::string criteria1;
int criteria2;
long criteria3;
You can declare getVal() with auto
template <int I>
const auto & getVal () const;
and define (with auto) all versions of getVal()
template <>
const auto & CustomClass::getVal<1> () const
{ return criteria1; }
template <>
const auto & CustomClass::getVal<2> () const
{ return criteria2; }
template <>
const auto & CustomClass::getVal<3> () const
{ return criteria3; }
and combining auto with decltype(), you can modify getDataByCriteria() in this way
template <int I>
auto getDataByCriteria (std::vector<CustomClass> aVector)
{
std::map<decltype(aVector[0].getVal<I>()), int> result;
for (const auto & cc : aVector)
{
if ( result.find(cc.getVal<I>()) == result.end()) // if such of key doesn't exists
{
result.insert(std::make_pair(cc.getVal<I>(), cc.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
}
}
return result;
}
The use of the function remain the same (thanks to auto again)
auto map1 = getDataByCriteria<1>(ccVec);
auto map2 = getDataByCriteria<2>(ccVec);
auto map3 = getDataByCriteria<3>(ccVec);
p.s.: caution: code not tested
p.s.2 : sorry for my bad English

You can use a function to extract a filed such as
std::string extractFiled(const CustomClass &object, int which) {
switch (which) {
case 1:
return object.criteria1;
case 2:
return object.criteria2;
case 3:
return object.criteria3;
default:
return object.criteria1;
}
}
and getDataByCriteria add an arg to indicate which filed to use.
Or you can just use macro to implement getDataByCriteria.

You tagged it C++11, so use variadic templates.
class VariadicTest
{
public:
VariadicTest()
{
std::map<std::string, int> test1 = getDataByCriteria(testValues, criteria1);
std::map<std::string, int> test2 = getDataByCriteria(testValues, criteria2);
std::map<std::string, int> test3 = getDataByCriteria(testValues, criteria1, criteria2);
std::map<std::string, int> test4 = getDataByCriteria(testValues, criteria1, criteria3);
}
private:
std::string criteria1 = { "Hello" };
std::string criteria2 = { "world" };
std::string criteria3 = { "." };
std::vector<CustomClass> testValues = { {"Hello",1}, {"world",2},{ "!",3 } };
template<typename T> std::map<std::string, int> getDataByCriteria(std::vector<CustomClass> values, T criteria)
{
std::map<std::string, int> result;
//do whatever is needed here to filter values
for (auto v : values)
{
if (v.identifier == criteria)
{
result[values[0].identifier] = values[0].value;
}
}
return result;
}
template<typename T, typename... Args> std::map<std::string, int> getDataByCriteria(std::vector<CustomClass> values, T firstCriteria, Args... args)
{
std::map<std::string, int> result = getDataByCriteria(values, firstCriteria);
std::map<std::string, int> trailer = getDataByCriteria(values, args...);
result.insert(trailer.begin(), trailer.end());
return result;
}
};

You do not specify the actual operations to be done under the various conditions of the criteria being met so it is hard to say how much they actually can be combined.
Here is a possible solution using the std::accumulate() of the STL along with some additional functionality. This example was compiled with Visual Studio 2015.
This approach would make sense if most of the functionality can be combined into a reasonably small accumulation function because most of the criteria are handled in the same way. Or you could have the accumulate_op() function call other functions for specific cases while handling the general case itself.
You might take this as a beginning and make the appropriate modifications.
One such modification may be to get rid of the use of std::map to maintain state. Since using this approach you would iterate through the std::vector doing the accumulation based on the criteria, I am not sure you would even need to use std::map to remember anything if you are accumulating as you go.
// map_fold.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <numeric>
// this is the class with data and criteria
class CustomClass
{
public:
CustomClass() : dataToBeCombined(0) {}
std::string criteria1;
std::string criteria2;
std::string criteria3;
//... and others criteria
int dataToBeCombined;
// other code
};
// This is the class that will contain the results as we accumulate across the
// vector of CustomClass items.
class Criteria_Result {
public:
Criteria_Result() : dataToBeCombined(0) {}
CustomClass myCriteria;
std::map<std::string, int> result1;
std::map<std::string, int> result2;
std::map<std::string, int> result3;
int dataToBeCombined;
};
// This is the accumulation function we provide to std::accumulate().
// This function will build our results.
class accumulate_op {
public:
Criteria_Result * operator ()(Criteria_Result * x, CustomClass &item);
};
Criteria_Result * accumulate_op::operator ()(Criteria_Result *result, CustomClass &item)
{
if (!result->myCriteria.criteria1.empty() && !item.criteria1.empty()) {
std::map<std::string, int>::iterator it1 = result->result1.find(item.criteria1);
if (it1 == result->result1.end()) // if such of key doesn't exists
{
result->result1.insert(std::make_pair(item.criteria1, item.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
it1->second += item.dataToBeCombined;
}
result->dataToBeCombined += item.dataToBeCombined;
}
if (!result->myCriteria.criteria2.empty() && !item.criteria2.empty()) {
std::map<std::string, int>::iterator it2 = result->result2.find(item.criteria2);
if (it2 == result->result2.end()) // if such of key doesn't exists
{
result->result2.insert(std::make_pair(item.criteria2, item.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
it2->second += item.dataToBeCombined;
}
result->dataToBeCombined += item.dataToBeCombined;
}
if (!result->myCriteria.criteria3.empty() && !item.criteria3.empty()) {
std::map<std::string, int>::iterator it3 = result->result3.find(item.criteria3);
if (it3 == result->result3.end()) // if such of key doesn't exists
{
result->result3.insert(std::make_pair(item.criteria3, item.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
it3->second += item.dataToBeCombined;
}
result->dataToBeCombined += item.dataToBeCombined;
}
return result;
}
int main()
{
Criteria_Result result;
std::vector<CustomClass> aVector;
// set up the criteria for the search
result.myCriteria.criteria1 = "string1";
result.myCriteria.criteria2 = "string2";
for (int i = 0; i < 10; i++) {
CustomClass xx;
xx.dataToBeCombined = i;
if (i % 2) {
xx.criteria1 = "string";
}
else {
xx.criteria1 = "string1";
}
if (i % 3) {
xx.criteria2 = "string";
}
else {
xx.criteria2 = "string2";
}
aVector.push_back (xx);
}
// fold the vector into our results.
std::accumulate (aVector.begin(), aVector.end(), &result, accumulate_op());
std::cout << "Total Data to be combined " << result.dataToBeCombined << std::endl;
std::cout << " result1 list " << std::endl;
for (auto jj : result.result1) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
std::cout << " result2 list " << std::endl;
for (auto jj : result.result2) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
std::cout << " result3 list " << std::endl;
for (auto jj : result.result3) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
std::cout << " Trial two \n\n" << std::endl;
result.myCriteria.criteria2 = "";
result.result1.clear();
result.result2.clear();
result.result3.clear();
result.dataToBeCombined = 0;
// fold the vector into our results.
std::accumulate(aVector.begin(), aVector.end(), &result, accumulate_op());
std::cout << "Total Data to be combined " << result.dataToBeCombined << std::endl;
std::cout << " result1 list " << std::endl;
for (auto jj : result.result1) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
std::cout << " result2 list " << std::endl;
for (auto jj : result.result2) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
std::cout << " result3 list " << std::endl;
for (auto jj : result.result3) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
return 0;
}
This produces the output as follows:
Total Data to be combined 90
result1 list
string 25
string1 20
result2 list
string 27
string2 18
result3 list
Trial two
Total Data to be combined 45
result1 list
string 25
string1 20
result2 list
result3 list

Related

Generating floating point limits at compile time via template arguments and constexpr semantics:

I'm working on a set of classes. My Function class will take a Functor class which stores a function pointer to some defined function which has an operator that will invoke the function call from the function pointer. It uses a Limit class that currently takes <int,int> for its upper and lower bounds. It has nothing but static constexpr functions to return the bounds and to calculate the number of elements between those bounds. If the lower bounds = 1 and upper bounds = 5 it will generate 5 for the number of elements to be evaluated for that function...
Here is what I'm doing with these classes:
First I declare a function such as f(x) = x, f(x) = x^2, or f(x) = cos(x), etc.
Then I instantiate a Functor object based on the above function(s) parameter types both for the return and for its parameter-argument types...
Next, I assign the function to my Functor class's member variable.
Then I instantiate a Function object giving it the data-type and the Lower & Upper limits for the range of the function.
The Function class upon construction automatically generates the data points of that function from [lower,upper] and stores the generated values in its internal array.
The Function class also contains an operator that will allow the user to get any value from any given input.
Pseudo Example:
f(x) = x^2;
Functor<T,T> functor;
functor.member = &f(x);
Function<T,Lower,Upper,T> function(functor);
// If T=int, Lower = -4, and Upper = 4 then the internal data set will be
// (-4,16) (-3,9), (-2,4), (-1,1), (0,0), (1,1), (2,4), (3,9), (4,16)
// The user can also use it's operator to call function(9) and it will return 81
Here is my working program that is generating datasets of values from my classes using various functions:
main.cpp
#include <cmath>
#include <exception>
#include <iostream>
#include "Function.h"
int main() {
try {
pipes::Functor<int, int> functor1;
functor1.FuncPtr = &square;
pipes::Function<int, -10, 10, int> func1( functor1 );
auto data1{ func1.data() };
for (auto& p : data1)
std::cout << '(' << p.first << ',' << p.second << ")\n";
std::cout << '\n';
std::cout << "f(25) = " << func1(25) << "\n\n";
pipes::Functor<int, int> functor2;
functor2.FuncPtr = &linear;
pipes::Function<int, -10, 10, int> func2(functor2);
auto data2{ func2.data() };
for (auto& p : data2)
std::cout << '(' << p.first << ',' << p.second << ")\n";
std::cout << '\n';
std::cout << "f(25) = " << func2(25) << "\n\n";
pipes::Functor<double, double> functor3;
functor3.FuncPtr = &cosine;
pipes::Function<double, -7, 7, double> func3(functor3);
auto data3{ func3.data() };
for (auto& p : data3)
std::cout << '(' << p.first << ',' << p.second << ")\n";
std::cout << '\n';
std::cout << "f(25) = " << func3(25) << "\n\n";
}
catch (const std::exception& e) {
std::cerr << e.what() << "\n\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Function.h
#pragma once
#include <array>
namespace pipes {
template<typename Ret, typename... Args>
struct Functor {
Ret(*FuncPtr)(Args...);
Ret operator()(Args... args) { return FuncPtr(args...); }
};
template<int Lower, int Upper>
class Limits {
public:
static constexpr unsigned lower_bound() { return Lower; }
static constexpr unsigned upper_bound() { return Upper; }
static constexpr unsigned element_count() { return (Upper - Lower + 1); }
};
template<typename T, int Lower, int Upper, typename... Args>
class Function {
std::array<std::pair<T, T>, Limits<Lower,Upper>::element_count()> data_points_;
Functor<T,Args...> functor_;
public:
Function(Functor<T,Args...> func) {
functor_ = func;
for (unsigned i = 0; i < Limits<Lower,Upper>::element_count(); i++) {
data_points_[i].first = ((T)i + (T)Lower);
data_points_[i].second = functor_(data_points_[i].first);
}
}
T operator()(Args... args) const {
return functor_.FuncPtr(args...);
}
constexpr auto lower() const { return Lower; }
constexpr auto upper() const { return Upper; }
constexpr auto count() const { return Limits<Lower,Upper>::element_count(); }
constexpr auto data() const { return data_points_; }
};
} // namespace pipes
When I run the program it is generating this output which appears to be correct:
Output
(-10,100)
(-9,81)
(-8,64)
(-7,49)
(-6,36)
(-5,25)
(-4,16)
(-3,9)
(-2,4)
(-1,1)
(0,0)
(1,1)
(2,4)
(3,9)
(4,16)
(5,25)
(6,36)
(7,49)
(8,64)
(9,81)
(10,100)
f(25) = 625
(-10,-10)
(-9,-9)
(-8,-8)
(-7,-7)
(-6,-6)
(-5,-5)
(-4,-4)
(-3,-3)
(-2,-2)
(-1,-1)
(0,0)
(1,1)
(2,2)
(3,3)
(4,4)
(5,5)
(6,6)
(7,7)
(8,8)
(9,9)
(10,10)
f(25) = 25
(-7,0.753902)
(-6,0.96017)
(-5,0.283662)
(-4,-0.653644)
(-3,-0.989992)
(-2,-0.416147)
(-1,0.540302)
(0,1)
(1,0.540302)
(2,-0.416147)
(3,-0.989992)
(4,-0.653644)
(5,0.283662)
(6,0.96017)
(7,0.753902)
f(25) = 0.991203
And now for my question where this becomes the tricky part...
With my code currently the way it is, everything is fine as long as my bounds [-a,b] are of an integral type...
Let's suppose on my last example such as with cos, what if I want to have my bounds from [-2pi,2pi] where the lower and upper limits are of floating-point types...
The Issue:
Currently in C++ this is non-standard and in most cases won't compile:
template<float val> // or template<double>
struct foo() {
constexpr float operator()() {
return val;
}
};
And the above prevents me from doing something like this:
constexpr double PI{ 6.28318531 };
pipes::Functor<double, double> functor3;
functor3.FuncPtr = &cosine;
pipes::Function<double, -PI, PI, double> func3(functor3);
auto data3{ func3.data() };
for (auto& p : data3)
std::cout << '(' << p.first << ',' << p.second << ")\n";
std::cout << '\n';
std::cout << "f(25) = " << func3(25) << "\n\n";
So if I want to be able to support floating-point types for my intervals of my Limits or Range class... What kind of alternative would there be if such a thing is currently possible in c++? Or would I just have to simply restructure the way my class templates are designed?
If the above is possible in some way during compile time via templates and constexpr semantics, then there is another issue that arises that will have to be taken into consideration and that would be the stepping interval for use with floating-point types to know how many data points there will be within the dataset... (basically calculating dx based on some stepping value which would be defined by the user, for example: (0.1, 0.001, etc...) and the number of data points would be calculated by the number of these divisions between [lower, upper]... However, if the stepping value is known at compile-time, then calculating the divisions should be simple enough... that's not a major concern. The bigger concern is being able to express floating-point constants at compile time for template evaluation...
Currently, with the way my code is with its design, I have hit a limit on its functionality... I'm not sure how to provide a similar interface to support a floating-point range that can be calculated and generated at compile time! Any bit of help or suggestions is welcomed!
I think the closest you can get to a construct like yours is:
#include <iostream>
#include <array>
constexpr const double PI_2{ 6.28318531 };
template<double const &lower, double const &upper>
void foo() {
static_assert(lower<upper, "invalid lower and upper value");
constexpr size_t size = (upper-lower);
std::array<int, size> test;
std::cout << lower << " " << upper << " " << test.size() << std::endl;
}
template<double const &V>
struct neg {
static constexpr double value = -V;
};
int main()
{
foo<neg<PI_2>::value, PI_2>();
return 0;
}
If you can always specify the type as first template argument you could have something like this:
template<typename T, T const &lower, T const &upper>
void foo() {
std::cout << lower << " " << upper << std::endl;
}
I didn't fully think it through, how to get the floating-point part and the other together, but I think it should be possible.
In modern C++ and how templates are currently designed, I had to slightly restructure my code. It's forcing me to have to use std::vector instead of std::array, because we can't use floating-point types as constant template arguments... So I ended up having to change two of my classes... I had to change my Limits class, and my Function class.
My Limits class now accepts a Type instead of constant-integral-type and it stores 3 member variables. It also has a default constructor and a user constructor. The functions are now just constexpr instead of being static.
My Function class now stores a Limits class object and data_points_ is no longer an std::array as it is now std::vector. It's constructor now also takes in a Limits object.
I had also taken into account for the step size for floating-point ranges.
Here is what my modified code looks like with its given output:
main.cpp
#include <cmath>
#include <iostream>
#include <exception>
#include "Function.h"
constexpr int square(int x) {
return x * x;
}
constexpr int linear(int x) {
return x;
}
double cosine(double x) {
return cos(x);
}
//template<float val>
struct foo {
float operator()(float val) { return val; }
};
int main() {
try {
pipes::Functor<int, int> functor1;
pipes::Limits<int> limit1(-10, 10, 1);
functor1.FuncPtr = &square;
pipes::Function<int, int, int> func1( limit1, functor1 );
auto data1{ func1.data() };
for (auto& p : data1)
std::cout << '(' << p.first << ',' << p.second << ")\n";
std::cout << '\n';
std::cout << "f(25) = " << func1(25) << "\n\n";
pipes::Functor<int,int> functor2;
pipes::Limits<int> limit2(-10, 10, 1);
functor2.FuncPtr = &linear;
pipes::Function<int, int, int> func2(limit2, functor2);
auto data2{ func2.data() };
for (auto& p : data2)
std::cout << '(' << p.first << ',' << p.second << ")\n";
std::cout << '\n';
std::cout << "f(25) = " << func2(25) << "\n\n";
constexpr double PI{ 6.28318531 };
pipes::Functor<double, double> functor3;
pipes::Limits<double> limits3( (-PI), PI, 0.1);
functor3.FuncPtr = &cosine;
pipes::Function<double, double, double> func3(limits3, functor3);
auto data3{ func3.data() };
for (auto& p : data3)
std::cout << '(' << p.first << ',' << p.second << ")\n";
std::cout << '\n';
std::cout << "f(25) = " << func3(25) << "\n\n";
}
catch (const std::exception& e) {
std::cerr << e.what() << "\n\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Function.h
#pragma once
#include <vector>
namespace pipes {
template<typename Ret, typename... Args>
struct Functor {
Ret(*FuncPtr)(Args...);
Ret operator()(Args... args) { return FuncPtr(args...); }
};
template<typename Ty>
class Limits {
private:
Ty Lower;
Ty Upper;
Ty Step;
public:
Limits() {}
Limits(Ty lower, Ty upper, Ty step) : Lower{ lower }, Upper{ upper }, Step{ step } {}
constexpr Ty lower_bound() { return Lower; }
constexpr Ty upper_bound() { return Upper; }
constexpr Ty step_size() { return Step; }
constexpr unsigned element_count() { return (unsigned)((Upper - Lower + 1)/Step); }
};
template<typename LimT, typename FuncT, typename... Args>
class Function {
Limits<LimT> limits_;
Functor<FuncT, Args...> functor_;
std::vector<std::pair<FuncT, FuncT>> data_points_;
public:
Function(Limits<LimT> limits, Functor<FuncT,Args...> func) {
limits_ = limits;
functor_ = func;
data_points_.resize( limits_.element_count() );
for (unsigned i = 0; i < limits_.element_count(); i++) {
auto x = limits_.lower_bound() + (i * limits_.step_size());
data_points_[i].first = (x);
data_points_[i].second = functor_(x);
}
}
FuncT operator()(Args... args) const {
return functor_.FuncPtr(args...);
}
constexpr auto lower() const { return limits_.lower_bound(); }
constexpr auto upper() const { return limits_.upper_bound(); }
constexpr auto count() const { return limits_.element_count(); }
constexpr auto step() const { return limits_.step_size(); }
constexpr auto data() const { return data_points_; }
};
} // namespace pipes
Output
(-10,100)
(-9,81)
(-8,64)
(-7,49)
(-6,36)
(-5,25)
(-4,16)
(-3,9)
(-2,4)
(-1,1)
(0,0)
(1,1)
(2,4)
(3,9)
(4,16)
(5,25)
(6,36)
(7,49)
(8,64)
(9,81)
(10,100)
f(25) = 625
(-10,-10)
(-9,-9)
(-8,-8)
(-7,-7)
(-6,-6)
(-5,-5)
(-4,-4)
(-3,-3)
(-2,-2)
(-1,-1)
(0,0)
(1,1)
(2,2)
(3,3)
(4,4)
(5,5)
(6,6)
(7,7)
(8,8)
(9,9)
(10,10)
f(25) = 25
(-6.28319,1)
(-6.18319,0.995004)
(-6.08319,0.980067)
(-5.98319,0.955336)
(-5.88319,0.921061)
(-5.78319,0.877583)
(-5.68319,0.825336)
(-5.58319,0.764842)
(-5.48319,0.696707)
(-5.38319,0.62161)
(-5.28319,0.540302)
(-5.18319,0.453596)
(-5.08319,0.362358)
(-4.98319,0.267499)
(-4.88319,0.169967)
(-4.78319,0.0707372)
(-4.68319,-0.0291995)
(-4.58319,-0.128844)
(-4.48319,-0.227202)
(-4.38319,-0.32329)
(-4.28319,-0.416147)
(-4.18319,-0.504846)
(-4.08319,-0.588501)
(-3.98319,-0.666276)
(-3.88319,-0.737394)
(-3.78319,-0.801144)
(-3.68319,-0.856889)
(-3.58319,-0.904072)
(-3.48319,-0.942222)
(-3.38319,-0.970958)
(-3.28319,-0.989992)
(-3.18319,-0.999135)
(-3.08319,-0.998295)
(-2.98319,-0.98748)
(-2.88319,-0.966798)
(-2.78319,-0.936457)
(-2.68319,-0.896758)
(-2.58319,-0.8481)
(-2.48319,-0.790968)
(-2.38319,-0.725932)
(-2.28319,-0.653644)
(-2.18319,-0.574824)
(-2.08319,-0.490261)
(-1.98319,-0.400799)
(-1.88319,-0.307333)
(-1.78319,-0.210796)
(-1.68319,-0.112153)
(-1.58319,-0.0123887)
(-1.48319,0.087499)
(-1.38319,0.186512)
(-1.28319,0.283662)
(-1.18319,0.377978)
(-1.08319,0.468517)
(-0.983185,0.554374)
(-0.883185,0.634693)
(-0.783185,0.70867)
(-0.683185,0.775566)
(-0.583185,0.834713)
(-0.483185,0.88552)
(-0.383185,0.927478)
(-0.283185,0.96017)
(-0.183185,0.983268)
(-0.0831853,0.996542)
(0.0168147,0.999859)
(0.116815,0.993185)
(0.216815,0.976588)
(0.316815,0.950233)
(0.416815,0.914383)
(0.516815,0.869397)
(0.616815,0.815725)
(0.716815,0.753902)
(0.816815,0.684547)
(0.916815,0.608351)
(1.01681,0.526078)
(1.11681,0.438547)
(1.21681,0.346635)
(1.31681,0.25126)
(1.41681,0.153374)
(1.51681,0.0539554)
(1.61681,-0.0460021)
(1.71681,-0.1455)
(1.81681,-0.243544)
(1.91681,-0.339155)
(2.01681,-0.431377)
(2.11681,-0.519289)
(2.21681,-0.602012)
(2.31681,-0.67872)
(2.41681,-0.748647)
(2.51681,-0.811093)
(2.61681,-0.865435)
(2.71681,-0.91113)
(2.81681,-0.947722)
(2.91681,-0.974844)
(3.01681,-0.992225)
(3.11681,-0.999693)
(3.21681,-0.997172)
(3.31681,-0.984688)
(3.41681,-0.962365)
(3.51681,-0.930426)
(3.61681,-0.889191)
(3.71681,-0.839072)
(3.81681,-0.780568)
(3.91681,-0.714266)
(4.01681,-0.640826)
(4.11681,-0.560984)
(4.21681,-0.475537)
(4.31681,-0.385338)
(4.41681,-0.291289)
(4.51681,-0.19433)
(4.61681,-0.0954289)
(4.71681,0.0044257)
(4.81681,0.104236)
(4.91681,0.203005)
(5.01681,0.299745)
(5.11681,0.393491)
(5.21681,0.483305)
(5.31681,0.56829)
(5.41681,0.647596)
(5.51681,0.720432)
(5.61681,0.78607)
(5.71681,0.843854)
(5.81681,0.893206)
(5.91681,0.933634)
(6.01681,0.964733)
(6.11681,0.986192)
(6.21681,0.997798)
(6.31681,0.999435)
(6.41681,0.991085)
(6.51681,0.972833)
(6.61681,0.94486)
(6.71681,0.907447)
(6.81681,0.860967)
(6.91681,0.805884)
(7.01681,0.742749)
(7.11681,0.672193)
f(25) = 0.991203
This is giving me the behavior that I want, however, I was trying to do the same thing using array... I'm guessing until C++ supports floating-point-constants as template arguments I'm going to have to settle with std::vector using heap allocations, instead of std::array and stack-cache friendly containers...

Sort two different vectors (that each have "active" bool) with one function

I create the base class which has the ACTIVE BOOL
class BaseTest{
public:
bool active = false;
BaseTest(){
// make most true
if ( getRand(0, 5) != 2){
active = true;
}
}
};
create two different child classes
class ChildTest_1: public BaseTest{
string child1 = "Is child 1";
public:
ChildTest_1(){
}
};
class ChildTest_2: public BaseTest{
string child2 = "Is NOT child 1";
public:
ChildTest_2(){
}
};
I want to be able to pass either child (or any vector with "ACTIVE") to this function and it will return the first inactive. I have a program that runs a lot of vectors of many objects and usually have a class that manages each object vector. It is becoming a pain and waste of repeated code to write this loop in every mgmt class. I want one that I can pass any vector that has objects with an active var.
I don't need sorting right now, but that was the closest term to what I need.
What I need is a function I can pass a vector to and it will return the first inactive object;
It would be even better if they did not need to share a base class as long
as each object in the vector has its own ACTIVE bool, but I can also make a simple base class that all would derive from.
int firstInactive(vector<BaseTest> & test ){
for ( int cnt = 0 ; cnt < test.size() ; cnt++ ){
if (!test[cnt].active){
cout << cnt << " Is inactive " <<endl;
// add actual sorting here if I need;
return cnt;
}
}
}
int main(int, char const**){
vector< ChildTest_1 > allTest1;
vector< ChildTest_2 > allTest2;
allTest1.resize(10);
allTest2.resize(10);
cout << "First inactive in alltest1 is " << firstInactive(allTest1) <<endl;
cout << "First inactive in alltest2 is " << firstInactive(allTest2) <<endl;
// as expected it says no known matching function call.
return 0 ;
}
I've searched and experimented for a few days now. I've read everything I could on polymorphism and templates, but cannot find an example that helps me.
You may use template (No base class required):
template <typename T>
auto firstInactive(const std::vector<T>& v)
// -> typename std::vector<T>::const_iterator // for c++11
{
return std::find_if(v.begin(), v.end(), [](const T& e) { return !e.active; });
}
and call it:
std::vector<ChildTest_1> allTest1(10);
std::vector<ChildTest_2> allTest2(10);
auto it1 = firstInactive(allTest1);
auto it2 = firstInactive(allTest2);
if (it1 != allTest1.end()) {
std::cout << "First inactive in alltest1 is "
<< std::distance(allTest1.cbegin(), it1) << std::endl;
}
if (it2 != allTest2.end()) {
std::cout << "First inactive in alltest2 is "
<< std::distance(allTest2.cbegin(), it2) << std::endl;
}
Demo
You can use a template:
#include <iostream>
#include <vector>
template <typename T>
T getFirstInactive(const std::vector<T>& v){
for (const auto& i : v){
if (!i.active) return i;
}
return T();
}
struct Foo{
bool active;
int x;
Foo() : active(true),x(0) {};
};
int main(){
auto v = std::vector<Foo>(10);
v[4].active = false;
v[4].x = 3;
std::cout << getFirstActive(v).x << std::endl;
}
output:
3
However, you probably dont want a copy, but a reference to the element. In that case it might be better to make the template return an iterator. (Also the template has to return something in case there is no inactive element, which is much nicer with iterators).
I experimented and came up with this solution based on both of your answers:
template <typename T>
int getFirstInactive(const std::vector<T> & obj){
int itr = 0;
for (const auto& i : obj){
if (!i.active){
return itr;
}
itr++;
}
return itr;
}
this returns the index number of the first inactive object, then all I need is to see if that index number is the same as the size, in which cash I push_back a new object. problem solved, thanks!

Is it possible to write one function for std::string and std::wstring?

I just wrote a simple utility function for std::string. Then I noticed that the function would look exactly the same if the std::string was a std::wstring or a std::u32string. Is it possible to use a template function here? I am not very familiar with templates, and std::string and std::wstring are templates themselves, which might be an issue.
template<class StdStringClass>
inline void removeOuterWhitespace(StdStringClass & strInOut)
{
const unsigned int uiBegin = strInOut.find_first_not_of(" \t\n");
if (uiBegin == StdStringClass::npos)
{
// the whole string is whitespace
strInOut.clear();
return;
}
const unsigned int uiEnd = strInOut.find_last_not_of(" \t\n");
strInOut = strInOut.substr(uiBegin, uiEnd - uiBegin + 1);
}
Is this a proper way to do it? Are there pitfalls with this idea. I am not talking about this function but the general concept of using a templated class StdStringClass and calling the usual std::string functions like find, replace, erase, etc.
Its a good Idea, But I'd build the template on top of std::basic_string rather then general StdStringclass
template<class T>
inline void removeOuterWhitespace(std::basic_string<T>& strInOut)
{
constexpr auto delim[] = {T(' '),T('\t'),T('\n'),T(0)};
const auto uiBegin = strInOut.find_first_not_of(delim);
if (uiBegin == std::basic_string<T>::npos)
{
// the whole string is whitespace
strInOut.clear();
return;
}
const auto uiEnd = strInOut.find_last_not_of(delim);
strInOut = strInOut.substr(uiBegin, uiEnd - uiBegin + 1);
}
I would also ditch the MSDN-style "inout" notation in favro for simpler name like str. programmer will guess themselves that str is the result since it is passed as non-const reference and function returns void.
also, I changed unsigned int to auto. all the standard C++ containers/strings return size_t when returning indexes. size_t might not be unsigned int. auto matches itself to the right return value.
Assuming your template works as expected (haven't checked...sorry), another option would be to wrap the function in class, and control which types of strings classes you'd like the function to be applied to using constructors.
EDIT: added illustrative framework
EDIT2 one that compiles (at least with vs2015) :-)
class StringType1;
class StringTypeN;
class str {
//template function
template<class StdStringClass>
inline void removeOuterWhitespace(StdStringClass & strInOut)
{
//.
//.
//.
}
public:
//constructors
str(StringType1 &s1) { removeOuterWhitespace(s1); }
//.
//.
//.
str(StringTypeN &sN) { removeOuterWhitespace(sN); }
};
int main() {
return 0;
}
EDIT3 Proof of concept
#include <iostream>
class incr {
//template function
template<class incrementor>
inline void removeOuterWhitespace(incrementor & n)
{
n++;
}
public:
//constructors
incr(int &n1) { removeOuterWhitespace(n1); }
incr(double &n1) { removeOuterWhitespace(n1); }
incr(float &n1) { removeOuterWhitespace(n1); }
};
int main() {
int n1 = 1;
double n2 = 2;
float n3 = 3;
std::cout << n1 << "\t" << n2 << "\t" << n3 << std::endl;
auto test1 = incr(n1);
auto test2 = incr(n2);
auto test3 = incr(n3);
//all variables modified
std::cout << "all variables modified by constructing incr" << std::endl;
std::cout << n1 << "\t" << n2 << "\t" << n3 << std::endl;
return 0;
}

C++ Map with two keys of different values

I need a map which can have two keys, of different data types, yet point to the same struct.
struct DataStruct {
SomeEnum keyEnum; // <---- key as enum
std::string keyString; // <----- a key as a string
int arbitrarydata;
int moredata;
}
Then I want a std::map I can look up like:
std::map<SomeEnum||std::string, DataStruct> dataMap;
dataMap[SomeEnum::AValue] = dataStruct1;
dataMap["mykey"] = dataStruct2;
Is this even possible or do I need to make 2 maps? Seems a waste. Or do I need to overload an operator or something?
You can use std::pair, like this:
#include <iostream>
#include <map>
#include <utility>
typedef enum {A, B, C} en;
int main ()
{
en myen = A;
std::map<std::pair<char,int>, int> mymap;
mymap.insert ( std::pair<std::pair<char, int>,int>(std::make_pair('a',myen),200) );
mymap.insert ( std::pair<std::pair<char, int>,int>(std::make_pair('z',30),400) );
// showing contents:
std::cout << "mymap contains:\n";
for (std::map<std::pair<char,int>, int>::iterator it=mymap.begin(); it!=mymap.end(); ++it)
std::cout << "(" << it->first.first << ", " << it->first.second <<
") => " << it->second << '\n';
return 0;
}
Not an answer in the question:
Note, that in C++11, you can use enum class, which in general can be more useful.
A std::map can only have keys of the same type, but you can trick it with whatever key logic you want. Just be sure that they can compare properly:
struct DataStruct {
struct Key {
std::string keyString;
SomeEnum keyEnum;
int type;
Key(SomeEnum a) : keyEnum(a), type(0) { }
Key(const char * a) : keyString(a), type(1) { }
bool operator<(const Key & o) const {
if (type != o.type) return type < o.type;
else return type == 0 ? keyEnum < o.keyEnum : keyString < o.keyString;
}
};
int data;
}
Then you can use it almost the way you wanted:
std::map<DataStruct::Key, DataStruct> dataMap;
dataMap[SomeEnum::AValue] = dataStruct1;
dataMap["mykey"] = dataStruct2;
You need to be sure that keys of different types don't point to the same data, thats why I first order them by type and then by their value.

iterating through multiset of structs

I'm not getting the syntax right. Lets say I have this...
#include <set>
...
struct foo{
int bar;
string test;
};
struct comp{
inline bool operator()(const foo& left,const foo& right){
return left.bar < right.bar;
}
};
int main(){
std::multiset<foo,comp> fooset;
std::multiset<foo,comp>::iterator it;
...//insert into fooset
for (it = fooset.begin(); it != fooset.end(); it++){
//how do i access int bar and string test of each element?
}
return 0;
}
How do i access int bar and string test of each element inside the for loop?
Thanks!
There is a good mnemonic rule that an iterator is a safe C++ abstraction for pointer.
So basically you access the elements through dereferencing syntax:
(*it).bar = 0;
it->test = "";
for (it = fooset.begin(); it != fooset.end(); it++)
{
foo const & f = *it; //const is needed if it is C++11
//use f, e.g
std:: cout << f.bar <<", " << f.test << std::endl;
}
In C++11, you could do this instead:
for(foo const & f : fooset)
{
//use f, e.g
std:: cout << f.bar <<", " << f.test << std::endl;
}