Related
As an example, I have the following function template:
template <typename X, typename Y, typename Z>
void f(X &x, Y &y, Z &z) { ... }
I need to write a user interface in the form of
void fxyz(std::string optionX, std::string optionY, std::string optionZ)
Here, optionX, optionY, optionZ can be "x1" or "x2", "y1" or "y2", "z1" or "z2" respectively. Every option corresponds to a different type, i.e., X1, X2, Y1, ... .Currently I implemented it like this:
template <typename Y, typename Z>
void fx(std::string &optionX, Y &y, Z &z)
{
if (optionX == "x1") {
X1 x; f <X1, Y, Z> (x, y, z); }
else {
X2 x; f <X2, Y, Z> (x, y, z); }
}
template <typename Z>
void fxy(std::string &optionX, std::string &optionY, Z &z)
{
if (optionY == "y1") {
Y1 y; fx <Y1, Z> (optionX, y, z); }
else {
Y2 y; fx <Y2, Z> (optionX, y, z); }
}
void fxyz(std::string &optionX, std::string &optionY, std::string &optionZ)
{
if (optionZ == "z1") {
Z1 z; fxy <Z1> (optionX, optionY, z); }
else {
Z2 z; fxy <Z2> (optionX, optionY, z); }
}
This seems a lot of work especially if there are more template parameters. Any easier way to achieve what I want?
Thanks!
Map into variants, then visit them.
std::variant<X1, X2> choose_X(std::string_view choice) {
if(choice == "x1") return X1();
else if(choice == "x2") return X2();
}
std::variant<Y1, Y2> choose_Y(std::string_view choice) {
if(choice == "y1") return Y1();
else if(choice == "y2") return Y2();
}
std::variant<Z1, Z2> choose_Z(std::string_view choice) {
if(choice == "z1") return Z1();
else if(choice == "z2") return Z2();
}
You are not getting out of writing some set of rules to get from strings to objects, of course. A possible variation is
std::map<std::string, std::function<std::variant<X1, X2>()>> choices_X{
{"x1", []() { return X1(); }},
{"x2", []() { return X2(); }}
};
Then simply
std::string choice_X, choice_Y, choice_Z;
std::visit(
[](auto&&... xs) -> decltype(auto) { return f(std::forward<decltype(xs)>(xs)...); },
choose_X(choice_X), choose_Y(choice_Y), choose_Z(choice_Z)
);
O(n^2) code length is now O(n) code length (in number of parameters).
there is my .hpp :
#include <random>
#include <ctime>
#include <cmath>
#ifndef RECUIT_HPP
#define RECUIT_HPP
template < class E, class Func, class TempSeq, class RandomY, class RNG>
E recuit_simule(const Func & phi, E x0, const TempSeq & T, const RandomY & Y, RNG & G, long unsigned N) {
std::uniform_real_distribution<double> U(0,1);
E y;
double u;
for(int i=0; i<N; i++) {
y=Y(x0, G);
u=U(G);
if(u <= fmin(1, exp((phi(x0) - phi(y))/T(N)))) {
x0=y;
}
}
return x0;
}
#endif
and my .cpp :
#include "recuit.hpp"
#include <iostream>
class Y {
private:
std::normal_distribution<double> N;
public:
Y() : N(0,1) {}
double operator()(const double & x, std::mt19937 & G) { return x + N(G); }
};
int main() {
auto phi=[](const double & x) { return x*x*x*x*x*x - 48*x*x; };
auto T=[] (long unsigned n) { return 10 * pow(0.9, n); };
Y A;
std::mt19937 G;
double x = recuit_simule(phi, 0, T, A, G, 1000);
std::cout << x << std::endl;
return 0;
}
When i compile my .cpp, I have the following error in my .hpp :
recuit.hpp:17:6: error: no match for call to ‘(const Y) (int&,
std::mersenne_twister_engine&)’
for the line :
y=Y(x0, G);
And I don't understand why
Y::operator() is not const so you cannot call it on a const object. So make the parameter Y mutable:
E recuit_simule(const Func & phi, E x0, const TempSeq & T, RandomY & Y, RNG & G, long unsigned N) {
// ^~~~~~~~~~~
// not const
Side note: your code is very confusion and difficult to read because you don't use a different notation for types and variables. E.g. Y is a type but also the name of a parameter. And you are not consistent: sometimes variables are on lowercase, sometimes are capitalized.
I know that doubles can't be template parameters so I'm looking for another way to create a function with several parameters. My current (obviously wrong) code looks like this:
template<double B1, double B2, double B3, double C1, double C2, double C3>
double sellmeier(const double wavelength) {
double refractive_index_sq = 1;
double lambda_sq = std::pow(wavelength, 2);
refractive_index_sq += B1*lambda_sq/(lambda_sq-C1);
refractive_index_sq += B2*lambda_sq/(lambda_sq-C2);
refractive_index_sq += B3*lambda_sq/(lambda_sq-C3);
return refractive_index_sq;
}
Is there a way to create a function that takes several non integral type parameters and one argument without making every parameter an argument too?
What You want here is a policy class which will supply your function with the required values (SpecificConstants).
struct SpecificConstants
{
static constexpr double b1 { 0.6961663 };
static constexpr double b2 { 0.4079426 };
static constexpr double b3 { 0.8974794 };
static constexpr double c1 { 0.0684043 * 0.0684043 };
static constexpr double c2 { 0.1162414 * 0.1162414 };
static constexpr double c3 { 9.896161 * 9.896161 };
};
Your function will only require this policy class (Constants)
template< typename Constants >
double sellmeier( const double wavelength )
{
double refractive_index_sq = 1;
double lambda_sq = std::pow( wavelength, 2 );
refractive_index_sq += Constants::b1 * lambda_sq / ( lambda_sq - Constants::c1 );
refractive_index_sq += Constants::b2 * lambda_sq / ( lambda_sq - Constants::c2 );
refractive_index_sq += Constants::b3 * lambda_sq / ( lambda_sq - Constants::c3 );
return refractive_index_sq;
}
Then, the function can be called like this:
sellmeier< SpecificConstants >( 2.0 );
In c++14 you can do the following, shown with one parameter for brevity's sake:
constexpr double GetB1(int b1Index)
{
switch (b1Index)
{
case 0: return 1.2345;
case 1: return 2.3456;
default: return 3.4567;
}
}
template<int B1Index>
double obviouslyNotSellmeier(const double wavelength) {
return wavelength * GetB1(B1Index);
}
obviouslyNotSellmeier<1>(0.123)
Though it becomes very unfriendly to the caller.
Is there a way to create a function that takes several non integral type parameters and one argument without making every parameter an argument too?
I don't know if it's a good idea but... yes, I suppose you can wrap your floating points values as static constant inside struct's.
Starting from C++11 (constexpr) you can simply define
struct X1 { static constexpr double value { 1.2 }; };
struct X2 { static constexpr double value { 2.3 }; };
struct X3 { static constexpr double value { 3.4 }; };
struct Y1 { static constexpr double value { 4.5 }; };
struct Y2 { static constexpr double value { 5.6 }; };
struct Y3 { static constexpr double value { 6.7 }; };
pass they as template parameter to sellmeier()
sellmeier<X1, X2, X3, Y1, Y2, Y3>(1.0);
and use the value of types inside sellmeier()
template <typename B1, typename B2, typename B3,
typename C1, typename C2, typename C3>
double sellmeier (const double wavelength)
{
double refractive_index_sq = 1;
double lambda_sq = std::pow(wavelength, 2);
refractive_index_sq += B1::value*lambda_sq/(lambda_sq-C1::value);
refractive_index_sq += B2::value*lambda_sq/(lambda_sq-C2::value);
refractive_index_sq += B3::value*lambda_sq/(lambda_sq-C3::value);
return refractive_index_sq;
}
Before C++11 (no constexpr available) the syntax, to define the wrapping structs, is a little more annoying: you have to initialize the const value outside of the body of the structs
struct X1 { static double const value; };
struct X2 { static double const value; };
struct X3 { static double const value; };
struct Y1 { static double const value; };
struct Y2 { static double const value; };
struct Y3 { static double const value; };
double const X1::value = 1.2;
double const X2::value = 2.3;
double const X3::value = 3.4;
double const Y1::value = 4.5;
double const Y2::value = 5.6;
double const Y3::value = 6.7;
As I don't need the parameters at compile time I realized a functor is the better/correct solution:
struct sellmeier {
sellmeier(double B1, double B2, double B3, double C1, double C2, double C3) :
B1(B1), B2(B2), B3(B3), C1(C1), C2(C2), C3(C3) {}
double operator()(const double wavelength) {
double refractive_index_sq = 1;
double lambda_sq = std::pow(wavelength, 2);
refractive_index_sq += B1 * lambda_sq / (lambda_sq - C1);
refractive_index_sq += B2 * lambda_sq / (lambda_sq - C2);
refractive_index_sq += B3 * lambda_sq / (lambda_sq - C3);
return refractive_index_sq;
}
private:
double B1, B2, B3, C1, C2, C3;
};
//functor with sellmeier coefficients for fused quartz
auto sellmeier_fused_quartz = sellmeier(0.6961663, 0.4079426, 0.8974794, 0.0684043*0.0684043, 0.1162414*0.1162414, 9.896161*9.896161);
As I don't need the parameters at compile time I realized a functor is the better/correct solution
In this case, if you can use C++11, you can simply write a lambda
#include <cmath>
int main ()
{
auto B1=0.6961663;
auto B2=0.4079426;
auto B3=0.8974794;
auto C1=0.0684043*0.0684043;
auto C2=0.1162414*0.1162414;
auto C3=9.896161*9.896161;
auto sellmeier = [=] (double const wavelength)
{
double refractive_index_sq = 1;
double lambda_sq = std::pow(wavelength, 2);
refractive_index_sq += B1*lambda_sq/(lambda_sq-C1);
refractive_index_sq += B2*lambda_sq/(lambda_sq-C2);
refractive_index_sq += B3*lambda_sq/(lambda_sq-C3);
return refractive_index_sq;
};
sellmeier(1.0);
}
Starting from C++14, you can simplify (IMHO) as follows
#include <cmath>
int main ()
{
auto sellmeier = [B1=0.6961663,
B2=0.4079426,
B3=0.8974794,
C1=0.0684043*0.0684043,
C2=0.1162414*0.1162414,
C3=9.896161*9.896161] (double const wavelength)
{
double refractive_index_sq = 1;
double lambda_sq = std::pow(wavelength, 2);
refractive_index_sq += B1*lambda_sq/(lambda_sq-C1);
refractive_index_sq += B2*lambda_sq/(lambda_sq-C2);
refractive_index_sq += B3*lambda_sq/(lambda_sq-C3);
return refractive_index_sq;
};
sellmeier(1.0);
}
I wrote a smal example illustrating the problem. solve_bs1_y and solve_bs2_y are implemented completely similar. The only difference is the function call: solve_bs*_z. Unfortunately, it seems impossible to pass a template as argument to replace the function call of solve_bs*_z. Consequently, I have to implement for each solve_bs*_z another solve_bs*_y. Is there a way to simplify the code so that I need just one implementation of solve_bs_y?
// Example program
#include <iostream>
#include <string>
template <int x, int y, int offs, class T>
float solve_bs1_z(T mat, float fS, float fT, float fU) {
return 1; // to keep it simple
}
template <int x, int y, int offs, class T>
float solve_bs2_z(T mat, float fS, float fT, float fU) {
return 2; // to keep it simple
}
// essentially the same as solve_bs2_y
template <int x, int offs, class T>
float solve_bs1_y(T mat, float fS, float fT, float fU) {
const float bs_s = 2;
return ( solve_bs1_z<x, 0, offs>(mat, fS, fT, fU)
+ solve_bs1_z<x, 1, offs>(mat, fS, fT, fU)
+ solve_bs1_z<x, 2, offs>(mat, fS, fT, fU))
* bs_s;
}
// essentially the same as solve_bs1_y
template <int x, int offs, class T>
float solve_bs2_y(T mat, float fS, float fT, float fU) {
const float bs_s = 2;
return ( solve_bs2_z<x, 0, offs>(mat, fS, fT, fU)
+ solve_bs2_z<x, 1, offs>(mat, fS, fT, fU)
+ solve_bs2_z<x, 2, offs>(mat, fS, fT, fU) )
* bs_s;
}
// these are called in the program ..
template<int offs, class T>
float solve_ffd_bs1(T mat, float fS, float fT, float fU) {
return solve_bs1_y<0, offs>(mat, fS, fT, fU) +
solve_bs1_y<1, offs>(mat, fS, fT, fU) +
solve_bs1_y<2, offs>(mat, fS, fT, fU);
}
template<int offs, class T>
float solve_ffd_bs2(T mat, float fS, float fT, float fU) {
return solve_bs2_y<0, offs>(mat, fS, fT, fU) +
solve_bs2_y<1, offs>(mat, fS, fT, fU) +
solve_bs2_y<2, offs>(mat, fS, fT, fU);
}
int main()
{
int mat[3][3][3] = {
{{1,2,3}, {4,5,6}, {7,8,9}},
{{11,2,3}, {14,5,6}, {17,8,9}},
{{21,2,3}, {24,5,6}, {27,8,9}}
};
solve_ffd_bs2<0>(mat, 1,2,3);
return 0;
}
The wrapper version without struct templates:
struct s1 {
template <int x, int y, int offs, class T>
static float solve_bs_z(T mat, float fS, float fT, float fU) {
return 1; // to keep it simple
}
};
struct s2 {
template <int x, int y, int offs, class T>
static float solve_bs_z(T mat, float fS, float fT, float fU) {
return 2; // to keep it simple
}
};
template <class Wrapper, int x, int offs, class T>
float solve_bs_y(T mat, float fS, float fT, float fU) {
const float bs_s = 2;
return ( Wrapper::template solve_bs_z<x, 0, offs>(mat, fS, fT, fU)
+ Wrapper::template solve_bs_z<x, 1, offs>(mat, fS, fT, fU)
+ Wrapper::template solve_bs_z<x, 2, offs>(mat, fS, fT, fU))
* bs_s;
}
and then call:
solve_bs_y<s1, 0, 1>(...);
I want to have class, which uses boost::geometry::index::rtree for spatial indexers. This class alone should know about boost, so I use something like this:
struct VeryImportantInfo
{
...
float x;
float y;
}
class Catalogue
{
...
public:
std::vector<std::shared_ptr<VeryImportantInfo> > FindIn(float x1, float x2, float y1, float y2);
protected:
using point = bg::model::point<float, 2, bg::cs::cartesian>;
using value = std::pair<point, std::shared_ptr<VeryImportantInfo> >;
using box = bg::model::box<point>;
boost::geometry::index::rtree< value, bgi::quadratic<16> > rtree;
}
std::vector<std::shared_ptr<VeryImportantInfo> > Catalogue::FindIn(float x1, float y1, float x2, float y2)
{
box query_box(point(x1, y1), point(x2, y2));
???
}
I don't know how to do query properly (Please don't look at this awful vector return by copy, it is just for example sake). I can do this:
std::vector<std::shared_ptr<VeryImportantInfo> > Catalogue::FindIn(float x1, float y1, float x2, float y2)
{
box query_box(point(x1, y1), point(x2, y2));
std::vector<value> result_s;
rtree.query(bgi::intersects(query_box), std::back_inserter(result_s));
std::vector<std::shared_ptr<VeryImportantInfo> > results;
results.reserve(result_s.size());
for( auto& p : result_s)
{
results.emplace_back(p.second);
}
return results;
}
I want to know, how can I get rid of internal copy(not return copy, results.emplace_back(p.second); - this one). Because I can have more that 10k results in result_s and it will be a waste.
Thank you!
Update to the comment
If the worry was about the temporary vector in the first place, just don't use one. You can use the qbegin()/qend() free functions from boost::geometry::index:
std::vector<std::shared_ptr<VeryImportantInfo> > Catalogue::FindIn(float x1, float y1, float x2, float y2)
{
box query_box(point(x1, y1), point(x2, y2));
auto b = bgi::qbegin(rtree, bgi::intersects(query_box)),
e = bgi::qend(rtree);
auto range = boost::make_iterator_range(b, e);
using namespace boost::adaptors;
return boost::copy_range<std::vector<std::shared_ptr<VeryImportantInfo>>>(
range | transformed([](value const& p) { return p.second; }));
}
In fact, if the rtree is known to be constant, you could even return the lazy range directly and not allocate even the single vector.
original/old answer text follows:
You cannot copy shared pointers without reference counting.
Of course, you could change the value pair to contain a reference to the shared_ptr, but instead you could then employ either raw references (std::reference_wrapper) or weak_ptr.
std::reference_wrapper<T>
Here's my take on it with raw references (just keep your important data around :)):
Live On Coliru
#include <iostream>
#include <vector>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/index/rtree.hpp>
namespace bg = boost::geometry;
namespace bgi = bg::index;
struct VeryImportantInfo {
float x;
float y;
};
VeryImportantInfo a = { 2, 2 };
VeryImportantInfo b = { 3, 3 };
VeryImportantInfo c = { 4, 4 };
class Catalogue
{
public:
Catalogue() {
rtree.insert(value(point(a.x, a.y), a));
rtree.insert(value(point(b.x, b.y), b));
rtree.insert(value(point(c.x, c.y), c));
}
std::vector<std::reference_wrapper<VeryImportantInfo> > FindIn(float x1, float x2, float y1, float y2);
protected:
using point = bg::model::point<float, 2, bg::cs::cartesian>;
using value = std::pair<point, std::reference_wrapper<VeryImportantInfo> >;
using box = bg::model::box<point>;
boost::geometry::index::rtree< value, bgi::quadratic<16> > rtree;
};
std::vector<std::reference_wrapper<VeryImportantInfo> > Catalogue::FindIn(float x1, float y1, float x2, float y2)
{
box query_box(point(x1, y1), point(x2, y2));
std::vector<value> result_s;
rtree.query(bgi::intersects(query_box), std::back_inserter(result_s));
std::vector<std::reference_wrapper<VeryImportantInfo> > results;
results.reserve(result_s.size());
for(auto& p : result_s) {
results.push_back(p.second);
}
return results;
}
int main() {
Catalogue cat;
for (VeryImportantInfo& vii : cat.FindIn(1,2,3,4))
std::cout << vii.x << ", " << vii.y << "\n";
}
std::weak_ptr<T>
Here is the same with weak_ptr<>. One could argue this doesn't solve much (because ref counting is still happening) but at least less work is required.
Live On Coliru
#include <iostream>
#include <memory>
#include <vector>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/index/rtree.hpp>
namespace bg = boost::geometry;
namespace bgi = bg::index;
struct VeryImportantInfo {
float x;
float y;
};
auto a = std::make_shared<VeryImportantInfo>(VeryImportantInfo{2, 2});
auto b = std::make_shared<VeryImportantInfo>(VeryImportantInfo{3, 3});
auto c = std::make_shared<VeryImportantInfo>(VeryImportantInfo{4, 4});
class Catalogue
{
public:
Catalogue() {
rtree.insert(value(point(a->x, a->y), a));
rtree.insert(value(point(b->x, b->y), b));
rtree.insert(value(point(c->x, c->y), c));
}
std::vector<std::weak_ptr<VeryImportantInfo> > FindIn(float x1, float x2, float y1, float y2);
protected:
using point = bg::model::point<float, 2, bg::cs::cartesian>;
using value = std::pair<point, std::shared_ptr<VeryImportantInfo> >;
using box = bg::model::box<point>;
boost::geometry::index::rtree< value, bgi::quadratic<16> > rtree;
};
std::vector<std::weak_ptr<VeryImportantInfo> > Catalogue::FindIn(float x1, float y1, float x2, float y2)
{
box query_box(point(x1, y1), point(x2, y2));
std::vector<value> result_s;
rtree.query(bgi::intersects(query_box), std::back_inserter(result_s));
std::vector<std::weak_ptr<VeryImportantInfo> > results;
results.reserve(result_s.size());
for(auto& p : result_s) {
results.push_back(p.second);
}
return results;
}
int main() {
Catalogue cat;
for (auto& vii_p : cat.FindIn(1,2,3,4))
if (auto vii = vii_p.lock())
std::cout << vii->x << ", " << vii->y << "\n";
}