Forum Replies Created
-
AuthorPosts
-
::
Can you provide me more info? Maybe, you have a minimal runnable example. For example, I’m confused that your class Equation deals with QString. I would expect some kind of number.
You will probably implement a switch statement based on the enumerators. Here is an alternative, using a dispatch table.
#include <cmath> #include <functional> #include <iostream> #include <map> int main(){ std::cout << std::endl; // dispatch table std::map< const char , std::function<double(double,double)> > dispTable{ {'+',[](double a, double b){ return a + b;} }, {'-',[](double a, double b){ return a - b;} }, {'*',[](double a, double b){ return a * b;} }, {'/',[](double a, double b){ return a / b;} } }; // do the math std::cout << "3.5+4.5= " << dispTable['+'](3.5,4.5) << std::endl; std::cout << "3.5-4.5= " << dispTable['-'](3.5,4.5) << std::endl; std::cout << "3.5*4.5= " << dispTable['*'](3.5,4.5) << std::endl; std::cout << "3.5/4.5= " << dispTable['/'](3.5,4.5) << std::endl; // add a new operation dispTable['^']= [](double a, double b){ return std::pow(a,b);}; std::cout << "3.5^4.5= " << dispTable['^'](3.5,4.5) << std::endl; std::cout << std::endl; };
I wrote a post about this technique: https://www.modernescpp.com/index.php/functional-in-c-dispatch-table.
::This sentence is misleading: “Even when other containers seem more suited, such as map for O(log N) lookup performance or a list for efficient insertion in the middle, a vector will usually still perform better for containers up to a few KB in size.”
The quote makes only sense if you assume that the elements of a vector are not sorted. In this case, the lookup complexity is O(N). If the vector is sorted, the lookup has also the complexity O(log N) because you can use a bisection strategy. Here is the interesting observation. Although, both lookup mechanism (map versus sorted vector) have the same complexity O(log N), a vector will outperform a map because of its cache-friendly layout.
::The sequence containers std::array and a std::vector must occupy a contiguous memory block.
For the associative containers such as std::map and std::unordered_map, there are no implementation requirements but performance requirements. Consequentially, measurements about their memory consumption cannot be generalized.
4. November 2022 at 17:23 in reply to: Article: The pool of talented C++ developers is running dry #105203::1. Here is the answer: std::array – Dynamic Memory, no Thanks
2. Here is the answer: C++ Core Guidelines: std::array and std::vector are your Friends
::- A value type is a type whose identity is based on its values and not on its identity. Typcial value types are money or date. I talk more about value types in my “Design Patterns and Architectural Patterns with C++” mentoring.
- You should use the specializations of std::swap if available.
- A user-defined swap member function is essential for the copy-and-swap idiom. The copy-and-swap idiom gives you transactional semantics for the copy assignment and move assignment operator. This will also be part of the “Design Patterns and Architectural Patterns with C++” mentoring. Here is a short code snippet.
class Cont { public: // ... Cont& operator = (const Cont& rhs); Cont& operator = (Cont&& rhs) noexcept; friend void swap(Cont& lhs, Cont& rhs) noexcept { swap(lhs.size, rhs.size); swap(lhs.pdata, rhs.pdata); } private: int* pData; std::size_t size; }; Cont& Cont::operator = (const Cont& rhs) { Cont tmp(rhs); swap(*this, tmp); return *this; } Cont& Cont::operator = (Cont&& rhs) { Cont tmp(std::move(rhs)); swap(*this, tmp); return *this; }
::Here are two ways using std::bind or lambda expressions:
#include <functional> #include <iostream> class ObserverInterface { public: virtual ~ObserverInterface() {} virtual void update(int message) = 0; }; class ObservableAdapter: public ObserverInterface { public: ObservableAdapter(std::function<void(int)> call): callable(call) {} void update(int message) { callable(message); } private: ObservableAdapter(); std::function<void(int)> callable; }; void dummy(int) { std::cout << "Do something as function\n"; } struct Dummy{ void operator()(int) const { std::cout << "Do something as function object\n"; } void func(int) const { std::cout << "Do something as a member function\n"; } }; int main() { ObservableAdapter observer{[](int){ std::cout << "Do something as lambda\n"; }}; observer.update(10); ObservableAdapter observer2{dummy}; observer2.update(10); ObservableAdapter observer3{Dummy()}; observer3.update(10); Dummy dummy; ObservableAdapter observer4{std::bind(&Dummy::func, &dummy, 5)}; observer4.update(10); ObservableAdapter observer5{[dummy](int){ dummy.func(5); }}; observer5.update(10); }
- std::bind creates a callable: std::bind(&Dummy::func, &dummy, 5). Because you invoke the function func on the object dummy, you have to provide it in the call.
- The lambda expressions binds the object dummy by copy and invokes the function func on it: [dummy](int){ dummy.func(5); }
Here is the example on Compiler Explorer: https://godbolt.org/z/z3GqWr1cz.
::const_cast is a code smell but sometimes you have to deal with legacy software and you have to adapt the constness/volatileness of your variable.
- You should prefer a const_cast to a C-cast (int i = (int) myValue;). A const_cast documents its intent, but not a C-cast.
- Modifying a const declared object by removing its constness is undefined behavior: https://godbolt.org/z/6GWbbK71M.
::Your callback mechanism seems pretty complicated, and I don’t know why you need objects. I replaced it with a simple polymorphic function wrapper: std::function<void(int)>.
#include <functional> #include <iostream> class ObserverInterface { public: virtual ~ObserverInterface() {} virtual void update(int message) = 0; }; class ObservableAdapter: public ObserverInterface { public: ObservableAdapter(std::function<void(int)> call): callable(call) {} void update(int message) { callable(message); } private: ObservableAdapter(); std::function<void(int)> callable; }; void dummy(int) { std::cout << "Do something as function\n"; } struct Dummy{ void operator()(int) const { std::cout << "Do something as function object\n"; } }; int main() { ObservableAdapter observer{[](int){ std::cout << "Do something as lambda\n"; }}; observer.update(10); ObservableAdapter observer2{dummy}; observer2.update(10); ObservableAdapter observer3{Dummy()}; observer3.update(10); }
Here is the running program in Compiler Explorer: https://godbolt.org/z/7E6jG7eae
::No, there is no member function to get the use count of std::weak_ptr. Interestingly, boost’s weak_ptr implementation supports a use count for it.
::We used gSOAP. Our server was capable of handling 4000 clients concurrently. This was a typical client/server architecture. The server was a typical reactor based on select, poll, or epoll that accepts it client’s request and dispatched in to concurrent handlers. This is all what I know after 15 years.
Once more, the reason for gSOAP as SOAP implementation was performance. The Python server and the Java server based on SOAP was slower.
::I had a similar discussion in my company in the past. We had a big client/server architecture in the automotive industry. Big means up to 4000 clients. Client and server sent permanently data. After a long evolution phase, we decided for SOAP on the client and the server as communication infrastructure. This means that we use C++ on the server, and Java/Python on the client. The reason for SOAP was that it is a XML based protocol and had rich functionality. C++’s gsoap implementation was faster than the Java ones and pretty mature.
We decided against the following protocols: sun RPC, XML-RPC, CORBA, REST, homemade solution.
::Make Volume a user-defind type. This improves readability and enforces type safety. Additionally, combine it with user-defined literals.
#include <iostream> #include <ostream> namespace Units{ class Volume{ public: explicit Volume(double i):litre(i){} friend Volume operator +(const Volume& a, const Volume& b){ return Volume(a.litre + b.litre); } friend Volume operator -(const Volume& a, const Volume& b){ return Volume(a.litre - b.litre); } friend std::ostream& operator<< (std::ostream &out, const Volume& myDist){ out << myDist.litre << " l"; return out; } private: double litre; }; namespace VolumeUnits { Volume operator "" _l(long double d){ return Volume(d); } Volume operator "" _dl(long double m){ return Volume(m/10); } Volume operator "" _cl(long double d){ return Volume(d/100); } Volume operator "" _ml(long double c){ return Volume(c/1000); } } } using namespace Units::VolumeUnits; Units::Volume getVolume() { return 0.5_l + 3.5_ml - 10.5_dl; } int main(){ std:: cout << '\n'; std::cout << "1.0_l: " << 1.0_l << '\n'; std::cout << "1.0_dl: " << 1.0_dl << '\n'; std::cout << "1.0_cl: " << 1.0_cl << '\n'; std::cout << "1.0_ml: " << 1.0_ml << '\n'; std::cout << '\n'; std::cout << "1.0_l + 2.3_ml - 3.0_dl - 0.4_cl: " << 1.0_l + 2.3_ml - 3.0_dl - 0.4_cl << '\n'; Units::Volume vol = getVolume(); auto vol1 = getVolume(); std::cout << "getVolume(): " << getVolume() << '\n'; std::cout << '\n'; }
This is only a sketch.
Consider using the units library from Matesuz Pusz. It will be incorporated into an upcoming C++ standard. The necessary units are already defined: https://github.com/mpusz/units/blob/master/docs/framework/units.rst
-
AuthorPosts
