As an old c99 person, I was often stubled upon the curly brakets initialization. In the `initializer_list`, I have to use {r, i} for a complex number. On the other hand, I have to use (r, i) for `complex` in the istream field. Here, I cut a part of my class that is able to run and give examples under codeblock 20.03 with MinGW 8.1.0.
#ifndef __tMatrix_class__
#define __tMatrix_class__
#include <iostream>
#include <initializer_list>
#include <iomanip>
#include <complex>
#include <sstream>
template <typename T> class tMatrix
{
public:
T *ptr;
int col, row, size;
inline T* begin() const {return ptr;}
inline T* end() const {return this->ptr + this->size;}
inline T operator()(const int i, const int j) const { return ptr[i*col+j]; } // r-value
inline T&operator()(const int i, const int j) { return ptr[i*col+j]; } //l-value
inline tMatrix(): col{0}, row{0}, size{0}, ptr{0} {;}
tMatrix(const int i, const int j): col(j), row(i), size(i*j) {
ptr = new T [this->size] ; }
tMatrix(const std::initializer_list< std::initializer_list<T> > s):tMatrix<T>(s.size(), s.begin()->size())
{
int j = 0;
for (const auto& i : s) { std::copy (i.begin(), i.end(), ptr + j*col); ++j ; }
}
tMatrix(const tMatrix<T>&a) : tMatrix<T>(a.row, a.col)
{
std::copy(a.begin(), a.end(), this->ptr);
}
tMatrix<T>& operator=(tMatrix<T>&&a)
{
this->col = a.col;
this->row = a.row;
delete [] this->ptr;
this->ptr = a.ptr;
a.ptr = nullptr;
return *this;
}
tMatrix<T>& operator=(const tMatrix<T>&a)
{
if (col==a.cpl && row==a.row) std::copy(a.begin(), a.end(), this->ptr);
else { tMatrix<T>&&v(a); *this = std::move(v);}
return *this;
}
tMatrix<T>& operator=(const std::initializer_list<std::initializer_list<T> > a)
{
tMatrix<T> &&v = a;
*this = std::move(v);
return *this;
}
~tMatrix() {delete [] this->ptr;}
void operator<<(const char*s)
{
std::stringstream ss;
ss.str(s);
for (int i=0; i<this->size; i++){
if (ss.good()) ss >> this->ptr[i];
else return;
}
}
}; //end of class tMatrix
template <typename X> std::ostream& operator<<(std::ostream&p, const tMatrix<X>&a)
{
p << std::fixed;
for (int i=0; i<a.row; i++) {
for (int j=0; j <a.col; j++) p << std::setw(12) << a(i, j);
p << std::endl;
}
return p;
}
using CMPLX = std::complex<double>;
using iMatrix = tMatrix<int>;
using rMatrix = tMatrix<double>;
using cMatrix = tMatrix< CMPLX >;
#endif
int main()
{
cMatrix cx(2,2);
cx = { { {1,2},{3,4} }, { {5,6}, {7,8} } };
std::cout << cx << std::endl;
cx << "(1,2) (3,4)";
std::cout << cx << std::endl;
return 0;
}
The above code renders correct format of complex number, and prints
$ ./ttt_mtx_init_fin_tmp.exe
(1.000000,2.000000)(3.000000,4.000000)
(5.000000,6.000000)(7.000000,8.000000)
(1.000000,2.000000)(3.000000,4.000000)
(5.000000,6.000000)(7.000000,8.000000)
But if I use the `()` in the initializer_list and `{}` in the istream filed, the results are all wrong. If I chagned the relavant part of main() to :
cx = { { (1,2),(3,4) }, { (5,6), (7,8) } };
std::cout << cx << std::endl;
cx << "{1,2} {3,4}";
std::cout << cx << std::endl;
Which renders all wrong values (compared with above):
$ ./ttt_mtx_init_fin_tmp.exe
(2.000000,0.000000)(4.000000,0.000000)
(6.000000,0.000000)(8.000000,0.000000)
(2.000000,0.000000)(4.000000,0.000000)
(6.000000,0.000000)(8.000000,0.000000)
I found it is rather confusion. So, my questions: is there a way to make these two expressions a same form? Many thanks for any helps.
I do not know any way to make std::istream::operator>> use { and } for std::complex, but if you are fine with using a helper, then you can replace the () in the input with {} and forward the input to the original operator>>:
#include <iostream>
#include <complex>
#include <sstream>
#include <algorithm>
template <typename T>
struct complex_reader {
std::complex<T>& target;
};
template <typename T>
complex_reader<typename T::value_type> get_complex_reader(T& t){ return {t};}
template <typename T>
std::istream& operator>>(std::istream& in,complex_reader<T> cr){
std::string input;
std::getline(in,input,'}'); // read till `}`
std::replace(input.begin(),input.end(),'{','(');
input += ')';
std::stringstream ss{input};
ss >> cr.target; // call the original >>
return in;
}
int main()
{
std::stringstream ss{"{2,2}"};
std::complex<double> x;
ss >> get_complex_reader(x);
std::cout << x;
}
Output:
(2,2)
However, you would have to write a similar helper to get consistent output (you may not provide an operator<< for std::complex<T> directly). Also note that the above implementation is a little simplistic. It reads from the stream until it encounters a }. For invalid input this may result in undesired effects and more sophisticated input validation is required.
Note that the operator>> takes the complex_helper by value to allow passing temporaries. Thats fine, because the member is a (non-const) reference.
This is not an answer, but a reasoning of my choice. After a series of cross conversions with `largest_prime_is_463035818`, I figured out what is my best choice for now (many thanks to his time and patience). A bottom line is becoming clear to me that I will not alter the input format of istream that is too much changed for pratical purpose, since file input is the major method to fetch data for a large matrix.
Under this constrain, I try to make the appearance of initializer_list as friendly as possible. I did some experiments, and found that the complex_literals expression is acceptable by initializer_list. And it looks ok to me.
using namespace std::complex_literals;
int main()
{
cMatrix cx(3,2);
cx = { { 1+2.2j , 4j}, { 5.3+6.5j , 8.3j}, {8.3, 5.6+4j} };
std::cout << cx << std::endl;
cx << " (1,2) (3,4) (5,6) (7,8) (2.3, 3.4) (2,7.8) ";
std::cout << cx << std::endl;
return 0;
}
And it works.
$ ./a.exe
(1.000000,2.200000) (0.000000,4.000000)
(5.300000,6.500000) (0.000000,8.300000)
(8.300000,0.000000) (5.600000,4.000000)
(1.000000,2.000000) (3.000000,4.000000)
(5.000000,6.000000) (7.000000,8.000000)
(2.300000,3.400000) (2.000000,7.800000)
Thank you for your patience, and please let me know if there are better ways.
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 = □
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 = □
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...
So I am working on "TEMPLATES" and I'm required to make a 3 attempt of a function called PrintMax -it's obvious what it does-, to print the maximum element in an array of 3 elements, each attempt is for a different data type in this array -double/int/complex-. So I'm required to first, create the class Complex, and its required operator overloads, after that I use the PrintMax function as template function to work on the 3 types of arrays.
The problem here lies within the 3rd array of course, I can't write the elements of Complex into the array in this for ( a + bi ), because this is my class Complex :
class Complex
{
private :
int imaginary;
int real;
public:
Complex (int = 0, int = 0);
~Complex ();
int getImaginary();
int getReal();
void setImagniary(int i);
void setReal (int r);
bool operator > (Complex&);
};
You can notice, I overloaded operator > to check, but I also have a little problem besides not being able to write the elements in that way, the second problem is I can't -or sleepy and my brain is dying- calculate which is maximum in this array of Complex numbers :
// Input: Complex Array
// 1+3i, 2+4i, 3+3i
// Expected Output: 2+4i
So I want to assign them in the array with this form : Arr[3] = {1+3i, 2+4i, 3+3i};
Why is that the expected output, why not 3+3i ?
Thanks for reading ~
It seems to me that you are looking for something like:
template <typename T> void PrintMax(T array[])
{
// It is assumed that array has 3 elements in it.
std::cout <<
array[0] > array[1] ?
(array[0] > array[2] ? array[0] : array[2]) :
(array[1] > array[2] ? array[1] : array[2])
std::endl;
}
You could use something like the following. Note that there are no range checks in the code, it is just to demonstrate a way how you could solve your problem.
Plus i would suggest you to use a container (eg. std::vector) instead of plain arrays.
#include <algorithm>
#include <cmath>
#include <iostream>
class Complex {
private:
int imaginary;
int real;
public:
Complex(int r, int i) :
imaginary(i), real(r) {
}
~Complex() {
}
int getImaginary() const {
return imaginary;
}
int getReal() const {
return real;
}
void setImaginary(int i) {
imaginary = i;
}
void setReal(int r) {
real = r;
}
double getAbsolut() const {
return std::abs(std::sqrt(std::pow(imaginary, 2) + std::pow(real, 2)));
}
friend bool operator<(const Complex& lhs, const Complex& rhs);
friend std::ostream& operator<<(std::ostream& stream,
const Complex& complex);
};
bool operator<(const Complex& lhs, const Complex& rhs) {
return lhs.getAbsolut() < rhs.getAbsolut();
}
std::ostream& operator<<(std::ostream& stream, const Complex& complex) {
stream << "Complex(" << complex.real << "+" << complex.imaginary
<< "i)";
return stream;
}
template<int size, class T>
void getMax(const T arr[]) {
T max_value = arr[0];
for (size_t i = 1; i < size; ++i) {
max_value = std::max(max_value, arr[i]);
}
std::cout << "max: " << max_value << std::endl;
}
int main(int argc, char **argv) {
Complex arr_complex[] = { Complex(3, 3), Complex(2, 4), Complex(1, 3) };
int arr_int[] = { 3, 5, 1 };
double arr_double[] = { 2.3, 5.6, 9.1 };
getMax<3>(arr_complex);
getMax<3>(arr_int);
getMax<3>(arr_double);
return 0;
}
How do I have properties in C++ class, as you have in a C# class.
I don't want to have getter and setter methods.
You can use a solution similar to that Jon suggested, yet retaining ordinary C++ semantics using operator overloading. I've slightly modified Jon's code as following (explanations follow the code):
#include <iostream>
template<typename T>
class Accessor {
public:
explicit Accessor(const T& data) : value(data) {}
Accessor& operator=(const T& data) { value = data; return *this; }
Accessor& operator=(const Accessor& other) { this->value = other.value; return *this; }
operator T() const { return value; }
operator T&() { return value; }
private:
Accessor(const Accessor&);
T value;
};
struct Point {
Point(int a = 0, int b = 0) : x(a), y(b) {}
Accessor<int> x;
Accessor<int> y;
};
int main() {
Point p;
p.x = 10;
p.y = 20;
p.x++;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 15;
std::cout << p.x << "," << p.y << std::endl;
return 0;
}
We overload operator= to retain the usual assignment syntax instead of a function-call-like syntax. We use the cast operator as a "getter". We need the second version of the operator= to allow assignment of the second kind in main().
Now you can add to Accessor's constructor function pointers, or better - functors - to call as getters/setters in any way seems right to you. The following example assumes the setter function return bool to convey agreement to setting the new value, and the getter can just modify it on it's way out:
#include <iostream>
#include <functional>
#include <cmath>
template<typename T>
class MySetter {
public:
bool operator()(const T& data)
{
return (data <= 20 ? true : false);
}
};
template<typename T>
class MyGetter {
public:
T operator()(const T& data)
{
return round(data, 2);
}
private:
double cint(double x) {
double dummy;
if (modf(x,&dummy) >= 0.5) {
return (x >= 0 ? ceil(x) : floor(x));
} else {
return (x < 0 ? ceil(x) : floor(x));
}
}
double round(double r, int places) {
double off = pow(10.0L, places);
return cint(r*off)/off;
}
};
template<typename T, typename G = MyGetter<T>, typename S = MySetter<T>>
class Accessor {
public:
explicit Accessor(const T& data, const G& g = G(), const S& s = S()) : value(data), getter(g), setter(s) {}
Accessor& operator=(const T& data) { if (setter(data)) value = data; return *this; }
Accessor& operator=(const Accessor& other) { if (setter(other.value)) this->value = other.value; return *this; }
operator T() const { value = getter(value); return value;}
operator T&() { value = getter(value); return value; }
private:
Accessor(const Accessor&);
T value;
G getter;
S setter;
};
struct Point {
Point(double a = 0, double b = 0) : x(a), y(b) {}
Accessor<double> x;
Accessor<double> y;
};
int main() {
Point p;
p.x = 10.712;
p.y = 20.3456;
p.x+=1;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 15.6426;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 25.85426;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 19.8425;
p.y+=1;
std::cout << p.x << "," << p.y << std::endl;
return 0;
}
However, as the last line demonstrates it has a bug. The cast operator returning a T& allows users to bypass the setter, since it gives them access to the private value. One way to solve this bug is to implement all the operators you want your Accessor to provide. For example, in the following code I used the += operator, and since I removed the cast operator returning reference I had to implement a operator+=:
#include <iostream>
#include <functional>
#include <cmath>
template<typename T>
class MySetter {
public:
bool operator()(const T& data) const {
return (data <= 20 ? true : false);
}
};
template<typename T>
class MyGetter {
public:
T operator() (const T& data) const {
return round(data, 2);
}
private:
double cint(double x) const {
double dummy;
if (modf(x,&dummy) >= 0.5) {
return (x >= 0 ? ceil(x) : floor(x));
} else {
return (x < 0 ? ceil(x) : floor(x));
}
}
double round(double r, int places) const {
double off = pow(10.0L, places);
return cint(r*off)/off;
}
};
template<typename T, typename G = MyGetter<T>, typename S = MySetter<T>>
class Accessor {
private:
public:
explicit Accessor(const T& data, const G& g = G(), const S& s = S()) : value(data), getter(g), setter(s) {}
Accessor& operator=(const T& data) { if (setter(data)) value = data; return *this; }
Accessor& operator=(const Accessor& other) { if (setter(other.value)) this->value = other.value; return *this; }
operator T() const { return getter(value);}
Accessor& operator+=(const T& data) { if (setter(value+data)) value += data; return *this; }
private:
Accessor(const Accessor&);
T value;
G getter;
S setter;
};
struct Point {
Point(double a = 0, double b = 0) : x(a), y(b) {}
Accessor<double> x;
Accessor<double> y;
};
int main() {
Point p;
p.x = 10.712;
p.y = 20.3456;
p.x+=1;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 15.6426;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 25.85426;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 19.8425;
p.y+=1;
std::cout << p.x << "," << p.y << std::endl;
return 0;
}
You'll have to implements all the operators you're going to use.
For behaviour that's kind of like this, I use a templated meta-accessor. Here's a highly simplified one for POD types:
template<class T>
struct accessor {
explicit accessor(const T& data) : value(data) {}
T operator()() const { return value; }
T& operator()() { return value; }
void operator()(const T& data) { value = data; }
private:
accessor(const accessor&);
accessor& operator=(const accessor&);
T value;
};
Typical usage is like this:
struct point {
point(int a = 0, int b = 0) : x(a), y(b) {}
accessor<int> x;
accessor<int> y;
};
point p;
p.x(10);
p.y(20);
p.x()++;
std::cout << p.x();
The compiler typically inlines these calls if you set things up right and have optimisation turned on. It's no more of a performance bottleneck than using actual getters and setters, no matter what optimisations happen. It is trivial to extend this to automatically support non-POD or enumerated types, or to allow callbacks to be registered for whenever data are read or written.
Edit: If you prefer not to use the parentheses, you can always define operator=() and an implicit cast operator. Here's a version that does just that, while also adding basic "stuff happened" callback support:
Further Edit: Okay, totally missed that someone already made a revised version of my code. Sigh.
If you don't care that your C++ code won't compile with anything other than the Microsoft Visual C++ compiler, then you can use some of the compiler's non-standard extensions.
For instance, the following code will create a C#-like property called MyProperty.
struct MyType
{
// This function pair may be private (for clean encapsulation)
int get_number() const { return m_number; }
void set_number(int number) { m_number = number; }
__declspec(property(get=get_number, put=set_number)) int MyProperty;
private:
int m_number:
}
int main()
{
MyType m;
m.MyProperty = 100;
return m.MyProperty;
}
More information on this Microsoft-specific language extension is available here.
Here's a PoC implementation I did a while back, works nicely except that you need to set something up in the constructor for it to work nice and smoothly.
http://www.codef00.com/code/Property.h
Here's the example usage:
#include <iostream>
#include "Property.h"
class TestClass {
public:
// make sure to initialize the properties with pointers to the object
// which owns the property
TestClass() : m_Prop1(0), m_Prop3(0.5), prop1(this), prop2(this), prop3(this) {
}
private:
int getProp1() const {
return m_Prop1;
}
void setProp1(int value) {
m_Prop1 = value;
}
int getProp2() const {
return 1234;
}
void setProp3(double value) {
m_Prop3 = value;
}
int m_Prop1;
double m_Prop3;
public:
PropertyRW<int, TestClass, &TestClass::getProp1, &TestClass::setProp1> prop1;
PropertyRO<int, TestClass, &TestClass::getProp2> prop2;
PropertyWO<double, TestClass, &TestClass::setProp3> prop3;
};
and some usage of this class...
int main() {
unsigned int a;
TestClass t;
t.prop1 = 10;
a = t.prop1;
t.prop3 = 5;
a = t.prop2;
std::cout << a << std::endl;
return 0;
}
There are two annoyances with this approach:
You need to give the property a
pointer to its owning class.
The syntax to declare a property is a
bit verbose, but I bet I can clean
that up a bit with some macros
You don't. C++ doesn't support properties like C# does. If you want code to run on set/get, it will have to be a method.
Properties aren't supported in C++, but you can implement them:
1) By using templates
2) By making language extension and writing custom code preprocessor
Either approach won't be easy, but it can be done.
You could provide get and set methods that have similar names to the data members:
class Example
{
private:
unsigned int x_;
double d_;
std::string s_s;
public:
unsigned int x(void) const
{ return x_;}
void x(unsigned int new_value)
{ x_ = new_value;}
double d(void) const
{ return d_;}
void d(double new_value)
{ d_ = new_value;}
const std::string& s(void) const
{ return s_;}
void s(const std::string& new_value)
{ s_ = new_value;}
};
Although this comes close, as it requires using '()' for each member, it doesn't meet the exact functionality of properties that Microsoft Languages provide.
The closest match for properties is to declare the data members as public.
Here’s a bit crude and simple implementation using a preprocessor macro to effortlessly generate nested classes that provide the functionality of getters and setters with the nice and clean syntax as if they were actual variables. No templates or function pointers are used (if that’s a plus), although your compiled program will have as many (sub)classes of the name property_* as there are PROPERTIES declarations, and like in Evan Teran’s solution, you need to give the property a reference to the owning class in the constructor.
More operators (operator++, operator+=) should be easy to add.
(I guess the reference to the owning class is ok, despite the circularity...? (x.property_age.x.property_age ...))
#include <iostream>
#include <stdexcept>
#define PROPERTY(type, name, owner, get_exprs, set_exprs) \
friend class property_ ##name; \
class property_ ##name { \
owner & x; \
public: \
property_ ##name (owner & init): x(init) {} \
operator type () { \
get_exprs \
} \
type operator= (const type value) { \
set_exprs \
return value; \
} \
} name ;
int current_year = 2020;
class person {
int year_of_birth; // Integer is signed to demonstrate argument validation
public:
// Remember to add each property and *this for them
person(): year_of_birth(0), age(*this) {}
const int& born() { return year_of_birth; }
// Remember the semicolons
PROPERTY(int, age, person,
/*get:*/ return current_year - x.year_of_birth; ,
/*set:*/ if (value < 0) throw std::invalid_argument("person::age : age cannot be negative");
x.year_of_birth = current_year - value; )
};
int main() {
person alice, bob;
alice.age = bob.age = 28;
alice.age = alice.age + 5;
//alice.age = -7; //throws
// Apparently the compiler does nice implicit conversion from
// (the macro-generated class) 'property_age' to 'int'
std::cout << "Alice: born: " << alice.born() << ", age: " << alice.age << '\n'
<< "Bob: born: " << bob.born() << ", age: " << bob.age << '\n'
<< "The mean of their ages is: " << (alice.age + bob.age) / 2.0 << '\n';
return 0;
}