/*-------------------------------------------------------------------*/
void demo_std_generic_types() {
showNote("Demo std generic types", nl);
showOp("array<int,4>",nl);
auto a = std::array<int, 4> { 1, 2, 3, 4 };
showArray(a); // works only for arrays
showSeqColl(a); // works for any sequential collection
showOp("vector<double>", nl);
std::vector<double> v = { 1.0, 1.5, 2.0, 2.5 };
std::cout << v << "\n"; // uses operator<< overload for vectors, above
showSeqColl(v); // any sequential collection
std::cout << formatColl(v, "v", "\n"); // any STL collection
showOp("std::map<string,int>", nl);
std::map<std::string, int> m {
{"zero", 0}, {"one", 1}, {"two", 2}, {"three", 3}
};
std::cout << formatColl(m, "m", "\n"); // any STL collection
showAssocColl(m); // coll elements must be std::pair<K,V>
showOp("std::unordered_map<string,int>", nl);
std::unordered_map<std::string, int> um {
{"zero", 0}, {"one", 1}, {"two", 2}, {"three", 3}
};
std::cout << formatColl(um, "um", "\n"); // any STL collection
showAssocColl(um); // coll elements must be std::pair<K,V>
}
--------------------------------------------------
Demo std generic types
--------------------------------------------------
--- array<int,4> ---
array<T,N> [1, 2, 3, 4]
Collection<T> [1, 2, 3, 4]
--- vector<double> ---
vector<T>: {
1, 1.5, 2, 2.5
}
Collection<T> [1, 1.5, 2, 2.5]
v: {
1, 1.5, 2, 2.5
}
--- std::map<string,int> ---
m: {
{one, 1}, {three, 3}, {two, 2}, {zero, 0}
}
Collection<K,V> {
{one, 1}, {three, 3}, {two, 2}, {zero, 0}
}
--- std::unordered_map<string,int> ---
um: {
{three, 3}, {zero, 0}, {one, 1}, {two, 2}
}
Collection<K,V> {
{three, 3}, {zero, 0}, {one, 1}, {two, 2}
}
/*-------------------------------------------------------------------
HelloTemplates.h defines HelloTemplates<T> class
- HelloTemplates<T> is a simple demonstration of a template
type that does'nt do anything very useful except to explain
syntax.
*/
/*-------------------------------------------------------------------
simple user-defined type to demonstrate template syntax
*/
#include "AnalysisGen.h"
using namespace Analysis;
template<typename T>
class HelloTemplates {
public:
HelloTemplates() = default;
HelloTemplates(T& tin) : t(tin) {};
HelloTemplates(const HelloTemplates<T>& t) = default;
HelloTemplates<T>& operator=(const HelloTemplates<T>&t) = default;
~HelloTemplates() = default;
T& value() { return t; }
void show();
private:
T t;
};
template<typename T>
void HelloTemplates<T>::show() {
std::cout << " HelloTemplates<T> {\n ";
std::cout << " type T: "
<< truncate(DisplayParams.trunc,typeid(t).name()); // show type
std::cout << ", size: " << sizeof(t); // show size on stack
std::cout << ", value: " << t;
std::cout << "\n }\n";
}
/* template method specialization */
template<>
void HelloTemplates<std::vector<int>>::show() {
std::cout << " HelloTemplates<T> {\n ";
std::cout << " type T: "
<< truncate(DisplayParams.trunc,typeid(t).name()); // show type
std::cout << ", size: " << sizeof(t) << "\n value: [ ";
for (auto item : t) {
std::cout << item << " ";
};
std::cout << "]\n }\n";
}
/* partial template specialization of HelloTemplates class */
template<template<typename> typename V, typename T>
class HelloTemplates<V<T>> {
HelloTemplates<V<T>>& operator=(const HelloTemplates<V<T>>& v) = default;
~HelloTemplates() = default;
V<T>& value() { return val; }
void show() {
std::cout << " HelloTemplates<V<T>> {\n ";
std::cout << " type V<T>: "
<< truncate(DisplayParams.trunc,typeid(val).name()); // show type
std::cout << ", size: " << sizeof(val) << "\n value: [ ";
for (auto item : val) {
std::cout << item << " ";
};
std::cout << "]\n }\n";
}
private:
V<T> val;
};
/*-- demonstrate creation and use of HelloTemplates<T> --*/
void demo_custom_type_HelloTemplates() {
println();
showNote("Demo user-defined HelloTemplates<T>", 40);
showOp("HelloTemplates<T>", nl);
int arg = 42;
HelloTemplates<int> demi(arg);
demi.show();
std::cout << std::endl;;
double pi = 3.1415927;
HelloTemplates<double> demd(pi);
demd.show();
/*-------------------------------------------------------
specialization defined in HelloTemplates<T> class header
HelloTemplates.h and used here
*/
auto vs = std::vector<int> { 1, 2, 3 };
HelloTemplates<std::vector<int>> demv(vs);
demv.show();
}
----------------------------------------
Demo user-defined HelloTemplates<T>
----------------------------------------
--- HelloTemplates<T> ---
HelloTemplates<T> {
type T: int, size: 4, value: 42
}
HelloTemplates<T> {
type T: double, size: 8, value: 3.14159
}
HelloTemplates<T> {
type T: class std::vector<int,class std::allocat..., size: 32
value: [ 1 2 3 ]
}
/*-------------------------------------------------------------------
Stats<T>
- Stats<T> holds a std::vector<T> and provides methods for
computing max, min, average of this collection
of unspecified type T
- Code builds as a template definition
- Will fail to build instantiation if T is not a numeric type
*/
#include <iostream>
#include <vector>
#include <exception>
#include <concepts>
#include "AnalysisGen.h"
using namespace Analysis;
/*-------------------------------------------------------------------
Stats<T> class provides several simple computational services on
a vector of items who's type provides required arithmetic operations.
- This class inhibits compiler generation of default constructor
and assignment operator.
*/
template <typename T>
concept Number = std::integral<T> || std::floating_point<T>;
template <typename T>
requires Number<T>
class Stats {
public:
Stats() = default;
Stats(const std::vector<T>& v);
Stats(const Stats<T>& s) = default;
Stats<T>& operator=(Stats<T>& s) = default;
size_t size();
T max();
T min();
T sum();
double avg();
void show(const std::string& name="");
private:
bool check();
const std::vector<T>& items;
};
/*-------------------------------------------------------------------
Constructor initialized with vector of values
*/
template<typename T>
requires Number<T>
Stats<T>::Stats(const std::vector<T>& v) : items(v) {}
/*-------------------------------------------------------------------
check that Stats instance contains at least one value
*/
template<typename T>
requires Number<T>
bool Stats<T>::check() {
return items.size() > 0;
}
/*-------------------------------------------------------------------
returns number of data items
*/
template<typename T>
requires Number<T>
size_t Stats<T>::size() {
if(!check()) {
throw "Stats is empty";
}
return items.size();
}
/*-------------------------------------------------------------------
returns largest value (not necessarily largerst magnitude)
*/
template<typename T>
requires Number<T>
T Stats<T>::max() {
if(!check()) {
throw "Stats is empty";
}
auto max = items[0];
for( auto item : items) {
if (item > max) {
max = item;
}
}
return max;
}
/*-------------------------------------------------------------------
returns smallest value (not necessarily smallest magnitude)
*/
template<typename T>
requires Number<T>
T Stats<T>::min() {
if(!check()) {
throw "Stats is empty";
}
auto min = items[0];
for( auto item : items) {
if (item < min) {
min = item;
}
}
return min;
}
/*-------------------------------------------------------------------
returns sum of data values
*/
template<typename T>
requires Number<T>
T Stats<T>::sum() {
if(!check()) {
throw "Stats is empty";
}
auto sum = T{0};
for( auto item : items) {
sum += item;
}
return sum;
}
/*-------------------------------------------------------------------
returns average of data values
*/
template<typename T>
requires Number<T>
double Stats<T>::avg() {
if(!check()) {
throw "Stats is empty";
}
auto sum = T{0};
for( auto item : items) {
sum += item;
}
return double(sum)/double(items.size());
}
/*-------------------------------------------------------------------
displays current contents
*/
template<typename T>
requires Number<T>
void Stats<T>::show(const std::string& name) {
if(!check()) {
throw "Stats is empty";
}
std::cout << "\n " << name << " {\n ";
auto iter = items.begin();
std::cout << *iter++;
while(iter != items.end()) {
std::cout << ", " << *iter++;
std::cout.flush();
}
std::cout << "\n }\n";
}
/*-- demonstrate custom type Stats<T> --*/
void demo_custom_type_Stats() {
println();
showNote("Demo user-defined Stats<T>", 35);
showOp("Stats<double> s(v)", nl);
std::vector<double> v { 1.0, 2.5, -3.0, 4.5 };
showSeqColl(v);
Stats<double> s(v);
std::cout << " min: " << s.min();
std::cout << ", max: " << s.max();
std::cout << ", sum: " << s.sum();
std::cout << ", avg: " << s.avg() << std::endl;
showOp("Stats<double> s2 = s", nl);
Stats<double> s2 = s; // copy construction
std::cout << " min: " << s2.min();
std::cout << ", max: " << s2.max();
std::cout << ", sum: " << s2.sum();
std::cout << ", avg: " << s2.avg() << std::endl;
showOp("Stats<int> s3(u)", nl);
std::vector<int> u { 1, 2, 3, 1 };
showSeqColl(u);
Stats<int> s3(u);
std::cout << " min: " << s3.min();
std::cout << ", max: " << s3.max();
std::cout << ", sum: " << s3.sum();
std::cout << ", avg: " << s3.avg() << std::endl;
/*--------------------------------------------------
This works without the Number concept, with the
exception of average. With concept the stats
library fails to compile because strings are
not ints or floats.
*/
// showOp("Stats<std::string> ss", nl);
// std::vector<std::string> vstr { "ab", "cd", "ef" };
// Stats<std::string> ss(vstr);
// std::cout << " min: " << ss.min();
// std::cout << ", max: " << ss.max();
// std::cout << ", sum: " << ss.sum();
//--------------------------------------------------
// first compile phase:
// Stats<T>::avg() passess
// second compile phase:
// Stats<std::string>::avg() fails to compile.
// No way to divide sum string by size integer in
// std::cout << ", avg: " << ss.avg() << std::endl;
// All the other methods compile successfully.
println();
}
-----------------------------------
Demo user-defined Stats<T>
-----------------------------------
--- Stats<double> s(v) ---
Collection<T> [1, 2.5, -3, 4.5]
min: -3, max: 4.5, sum: 5, avg: 1.25
--- Stats<double> s2 = s ---
min: -3, max: 4.5, sum: 5, avg: 1.25
--- Stats<int> s3(u) ---
Collection<T> [1, 2, 3, 1]
min: 1, max: 3, sum: 7, avg: 1.75
/*-------------------------------------------------------------------
PointsGen.h defines point classe Point<T, N>
- Point<T, N> represents points with N coordinates of
unspecified type T and a Time t.
*/
#include <iostream>
#include <vector>
#include <string>
#include <initializer_list>
#include <concepts>
#include "AnalysisGen.h"
#include "Time.h"
namespace Points {
//using namespace Analysis;
/*-------------------------------------------------------------------
Point<T, N> class represents a point in an N-Dimensional hyperspace.
It uses a template parameter to support a variety of coordinate
types, and uses a vector to hold any finite number of
coordinates, specified by N.
It also carries a Time t instance which conceptually is the time
at which something was at that point in space. Time is a class
defined for this demonstration in Time.h.
All its special members, ctors, assignment, ... with the exception
of constructor Point(), are declared default to indicate to a maintainer
that compiler generated methods are correct and should not be provided.
It does not provide an iterator nor begin() and end() members.
Those will added in the iteration bit.
*/
template<typename T, const size_t N>
class Point {
public:
Point(); // default ctor
Point(std::initializer_list<T> il); // construct from list
Point(const Point& pt) = default; // copy ctor
Point(Point&& pt) = default; // move ctor
Point& operator=(const Point& pt) = default; // copy assignment
Point& operator=(Point&& pt) = default; // move assignemnt
~Point() = default; // dtor
void init(const std::vector<T>& v);
std::string timeToString();
void updateTime();
Time& time();
const size_t size() const;
T& operator[](size_t index); // index oper
const T operator[](size_t index) const; // const index oper
std::vector<T>& coords() { return coord; } // accessor
void show(const std::string& name); // display contents
size_t& left() { return _left; } // display indent
size_t& width() { return _width; } // display width
private:
std::vector<T> coord;
Time tm;
size_t _left = 2; // default display indent
size_t _width = 7; // default display row width
};
/*-----------------------------------------------
Point<T, N> constructor with size Template
parameter
*/
template<typename T, size_t N>
Point<T, N>::Point()
: tm(Time()) {
for(size_t i=0; i<N; i++) {
coord.push_back(T{0});
}
}
/*-----------------------------------------------
Fill coor with elements from initializer list li
- if li is smaller than N then fill remainder with
default values of T
- if li is larger use first N elements of li
*/
template<typename T, size_t N>
Point<T, N>::Point(std::initializer_list<T> il)
: tm(Time()) {
size_t sz = std::min(N, il.size());
size_t i = 0;
for(auto item : il) {
coord.push_back(item);
if(++i == sz) {
break;
}
}
for(size_t i = il.size(); i<N; i++) {
coord.push_back(T{0});
}
}
/*---------------------------------------------
Always returns N
*/
template<typename T, size_t N>
const size_t Point<T, N>::size() const {
return coord.size();
}
/*---------------------------------------------
index returns mutable value
*/
template<typename T, size_t N>
T& Point<T, N>::operator[](size_t index) {
if (index < 0 || coord.size() <= index) {
throw "Point<T, N> indexing error";
}
return coord[index];
}
/*---------------------------------------------
index returns immutable value
*/
template<typename T, size_t N>
const T Point<T, N>::operator[](size_t index) const {
if (index < 0 || coord.len() <= index) {
throw "Point<T, N> indexing error";
}
return coord[index];
}
/*-----------------------------------------------
Fill coor with elements from vector v
- if v is smaller fill remainder with default
values of T
- if v is larger use first N elements of v
*/
template<typename T, size_t N>
void Point<T, N>::init(const std::vector<T>& v) {
size_t sz = std::min(N, v.size());
for(size_t i=0; i<sz; i++) {
coord[i] = v[i];
}
for(size_t i = v.size(); i<N; i++) {
coord[i] = T{0};
}
}
/*---------------------------------------------
returns string datetime
*/
template<typename T, size_t N>
std::string Point<T, N>::timeToString() {
std::string ts = tm.toString();
return ts;
}
/*---------------------------------------------
set time to current time
*/
template<typename T, size_t N>
void Point<T, N>::updateTime() {
tm = std::time(0);
}
/*---------------------------------------------
returns current number of seconds in clock's
epoch
*/
template<typename T, size_t N>
Time& Point<T, N>::time() {
return tm;
}
/*-----------------------------------------------
PointtN<T> display function
*/
template<typename T, size_t N>
void Point<T, N>::show(const std::string& name) {
std::cout << "\n" << indent(_left) << name << ": " << "Point<T, N>";
std::cout << " {\n";
std::cout << fold(coord, _left + 2, _width);
std::cout << indent(_left) << "}";
std::cout << "\n" << indent(_left) << tm.toString() << std::endl;
}
/*-----------------------------------------------
Overload operator<< required for
showType(Point<T, N> t, const std::string& nm)
*/
template<typename T, size_t N>
std::ostream& operator<<(std::ostream& out, Point<T, N>& t2) {
out << "\n" << indent(t2.left()) << "Point<T, N>";
out << " {\n";
out << fold(t2.coords(), t2.left() + 2, t2.width());
out << indent(t2.left()) << "}";
return out;
}
}
/*-- demonstrate use of Point type --*/
void demo_custom_type_Point() {
using namespace Analysis;
using namespace Points;
println();
showNote("Demo user-defined Point<T, N>", 40);
/*-- demonstrate Point<double 3> initialization lists --*/
showOp("Point<double, 3> p1 {1.0, 1.5, 2.0}"); // equal to N
Point<double, 3> p1 {1.0, 1.5, 2.0};
p1.show("p1");
std::cout << "\n p1[1] = " << p1[1]; // indexing
std::cout << "\n p1.time().day() = "
<< p1.time().day();
std::cout << "\n p1.time().seconds() = "
<< p1.time().seconds() << "\n";
showOp("Point<double, 3> p2 {1.0, 1.5}");
Point<double, 3> p2 {1.0, 1.5}; // less than N
p2.show("p2");
showOp("Point<double, 3> p3 {1.0, 1.5, 2.0, 2.5}");
Point<double, 3> p3 {1.0, 1.5, 2.0, 2.5}; // greater than N
p3.show("p3");
std::cout << "\n p3.timeToString():\n \""
<< p3.timeToString() << "\"\n";
showOp("Point<int, 10> p3 { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }");
Point<int, 10> p4 { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
p4.show("p4");
}
----------------------------------------
Demo user-defined Point
----------------------------------------
--- Point p1 {1.0, 1.5, 2.0} ---
p1: Point {
1, 1.5, 2
}
Thu May 9 20:10:50 2024 local time zone
p1[1] = 1.5
p1.time().day() = 9
p1.time().seconds() = 50
--- Point p2 {1.0, 1.5} ---
p2: Point {
1, 1.5, 0
}
Thu May 9 20:10:50 2024 local time zone
--- Point p3 {1.0, 1.5, 2.0, 2.5} ---
p3: Point {
1, 1.5, 2
}
Thu May 9 20:10:50 2024 local time zone
p3.timeToString():
"Thu May 9 20:10:50 2024 local time zone"
--- Point p3 { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } ---
p4: Point {
1, 2, 3, 4, 5, 6, 7,
8, 9, 10
}
Thu May 9 20:10:50 2024 local time zone
/*---------------------------------------------------------
Generic Function Definitions
*/
/*---------------------------------------------------------
showType(T t, ...)
- Display calling name, static class, and size
- requires DisplayParams
*/
template<typename T>
void showType(T t, const std::string &callname, const std::string& suffix) {
std::cout << "\n " << callname; // show name at call site
std::cout << " type: "
<< truncate(DisplayParams.trunc,typeid(t).name()); // show type
std::cout << "\n size: " << sizeof(t); // show size on stack
std::cout << suffix;
}
/*-------------------------------------------------------
show sequential collection
- requires integer indexer and size() function
- works for any sequential STL collection
*/
template<typename C>
void showSeqColl(const C& c) {
std::cout << " Collection<T> [";
std::cout << c[0];
for(size_t i=1; i<c.size(); ++i) {
std::cout << ", " << c[i];
}
std::cout << "]" << std::endl;
}
/*-------------------------------------------------------
show associative collection
- requires iterator
- elements must be std::pair<Key, Value>
- works for any associative STL collection
*/
template<typename C>
void showAssocColl(const C& c) {
std::cout << " Collection<K,V> {\n ";
bool first = true;
for(const auto& pair : c) {
if(first) {
std::cout << "{" << pair.first << ", " << pair.second << "}";
first = false;
}
else {
std::cout << ", {" << pair.first << ", " << pair.second << "}";
}
}
std::cout << "\n }\n";
}
/*-----------------------------------------------
Helper function for formatting output
- truncates line to N chars and adds ellipsis
*/
inline std::string truncate(size_t N, const char* pStr) {
std::string temp(pStr);
if(temp.length() > N) {
temp.resize(N);
return temp + "...";
}
return temp;
}
/*-----------------------------------------------
Helper function for formatting output
- generates string of n blanks to offset text
*/
inline std::string indent(size_t n) {
return std::string(n, ' ');
}
/*-----------------------------------------------
Helper function for formatting output
- folds lines after width elements
- used only in Point<T,N>::show()
*/
template<typename T>
std::string fold(std::vector<T>& v, size_t left, size_t width) {
std::stringstream out("\n");
out << indent(left);
for(size_t i=0; i<v.size(); ++i) {
if((i % width) == 0 && i != 0 && i != width - 1) {
out << "\n" << indent(left);
}
if(i < v.size() - 1) {
out << v[i] << ", ";
}
else {
out << v[i] << "\n";
break;
}
}
return out.str();
}
/*-----------------------------------------------
Helper function for formatColl
- defines out << std::pair<K,V>
- used in formatColl for associative containers
*/
template<typename K, typename V>
std::stringstream& operator<<(
std::stringstream& out, const std::pair<K,V>& p
) {
out << "{" << p.first << ", " << p.second << "}";
return out;
}
/*-----------------------------------------------
Format output for Collection types
- any type with iterator, begin(), and end()
like all the STL containers.
- elements need overload for operator<< as
implemented above
- folds into rows with width elements
- will replace folding logic with fold(...)
eventually
*/
template<typename Coll>
std::string formatColl(
const Coll& c, const std::string& nm, const std::string& suffix,
size_t left, size_t width
) {
std::stringstream out;
out << indent(left) << nm << ": {\n" << indent(left + 2);
size_t i = 0;
for(const Coll::value_type& elem : c) {
if((i % width) == 0 && i != 0 && i != width - 1) {
out << "\n" << indent(left + 2);
}
if(i < c.size() - 1) {
out << elem << ", ";
}
else {
out << elem << "\n" << indent(left) << "}" << suffix;
break;
}
++i;
}
return out.str();
}
/*-----------------------------------------------
Format output for scalar types like primitives
*/
template<typename T>
std::string formatScalar(
const T& t, const std::string& nm, const std::string& suffix,
size_t left
) {
std::stringstream out;
out << "\n" << indent(left) << nm << ": " << t << suffix;
return out.str();
}
/*-----------------------------------------------
Format output for strings
- indent and embed in quotation marks
*/
template<typename T>
std::string formatString(
const T& t, const std::string& nm, const std::string& suffix,
size_t left
) {
std::stringstream out;
out << "\n" << indent(left) << nm << ": \"" << t << "\"" << suffix;
return out.str();
}
/*-----------------------------------------------
Defines is_iterable trait
- uses template metaprogramming, e.g., user code
that evaluates during compilation
- detects STL containers and user-defined types
that provide iteration
https://stackoverflow.com/questions/13830158/check-if-a-variable-type-is-iterable
*/
template <typename T, typename = void>
struct is_iterable : std::false_type {};
// this gets used only when we can call
// std::begin() and std::end() on that type
template <typename T>
struct is_iterable<
T,
std::void_t
<decltype(std::begin(std::declval<T>())),
decltype(std::end(std::declval<T>()))>
> : std::true_type {};
template <typename T>
constexpr bool is_iterable_v = is_iterable<T>::value;
/*-----------------------------------------------
Displays almost everything.
- strings work better with formatString(...)
https://www.cppstories.com/2018/03/ifconstexpr/
Iteration is discussed in Bit Cpp_iter
*/
template<typename T>
std::string format(
const T& t, const std::string& nm, const std::string& suffix,
size_t left, size_t width
) {
if constexpr(is_iterable_v<T>) { // decision at compile-time
return formatColl(t, nm, suffix, left, width);
}
else {
return formatScalar(t, nm, suffix, left);
}
}
/*---------------------------------------------------------
Demonstrate functions
*/
void demo_generic_functions() {
showNote("demo generic functions", nl);
showOp("showType for std::string");
std::string s = "a string";
showType(s, "s", nl);
showOp("showType for std::vector");
std::vector<int> v {1, 2, 3, 2, 1, 0, -1, -2};
showType(v, "v", nl);
/*-------------------------------------------------------
showSeqColl works for any collection with
iterator, integer indexing, and size() function
*/
showOp("showSeqColl for std::string", nl);
showSeqColl(s);
showOp("showSeqColl for std::vector", nl);
showSeqColl(v);
/*-------------------------------------------------------
showAssocColl works for any collection with
interator and std::pair<key, Value> elements
*/
showOp("showAssocColl for std::map", "\n");
std::map<std::string, int> m {
{"zero", 0}, {"one", 1}, {"two", 2}, {"three", 3},
{"four", 4}, {"five", 5}
};
showAssocColl(m);
showOp("showAssocColl for std::unordered_map", "\n");
std::unordered_map<std::string, int> um1 {
{"zero", 0}, {"one", 1}, {"two", 2}, {"three", 3},
{"four", 4}, {"five", 5}
};
showAssocColl(um1);
/*-------------------------------------------------------
Analysis::format works for any collection with
interator and std::pair<key, Value> elements
*/
showOp("Analysis::format for int");
int mol = 42;
std::cout << Analysis::format(42, "mol", "\n");
// need to distinguish Analysis::format from std::format
showOp("Analysis::format for std::string");
std::cout << Analysis::format(s, "s", "\n");
showOp("Analysis::format for std::vector");
std::cout << Analysis::format(v, "v", "\n", 2, 5);
showOp("Analysis::format for std::map");
std::cout << Analysis::format(m, "m", "\n", 2, 4);
showOp("Analysis::format for std::unordered_map");
std::unordered_map<std::string, int> um2 {
{"zero", 0}, {"one", 1}, {"two", 2}, {"three", 4},
{"four", 4}, {"five", 5}
};
std::cout << Analysis::format(um2, "um", "\n", 2, 4);
}
--------------------------------------------------
demo generic functions
--------------------------------------------------
--- showType for std::string ---
s type: class std::basic_string<char,struct std:...
size: 28
--- showType for std::vector ---
v type: class std::vector<int,class std::allocat...
size: 16
--- showSeqColl for std::string ---
Collection<T> [a, , s, t, r, i, n, g]
--- showSeqColl for std::vector ---
Collection<T> [1, 2, 3, 2, 1, 0, -1, -2]
--- showAssocColl for std::map ---
Collection<K,V> {
{five, 5}, {four, 4}, {one, 1}, {three, 3}, {two, 2}, {zero, 0}
}
--- showAssocColl for std::unordered_map ---
Collection<K,V> {
{three, 3}, {zero, 0}, {one, 1}, {two, 2}, {five, 5}, {four, 4}
}
--- Analysis::format for int ---
mol: 42
--- Analysis::format for std::string --- s: {
a, , s, t, r, i, n,
g
}
--- Analysis::format for std::vector --- v: {
1, 2, 3, 2, 1,
0, -1, -2
}
--- Analysis::format for std::map --- m: {
{five, 5}, {four, 4}, {one, 1}, {three, 3},
{two, 2}, {zero, 0}
}
--- Analysis::format for std::unordered_map --- um: {
{three, 4}, {zero, 0}, {one, 1}, {two, 2},
{five, 5}, {four, 4}
}
/*-------------------------------------------------------------------
Time.h defines Time class to manage datetime strings
- Uses chrono to implement class for updateable time instances
Note: Add callable function for end of time period
*/
#include <iostream>
#include <string>
#include <chrono>
#include <ctime>
namespace Points {
/*---------------------------------------------
Time manages calendar times
*/
class Time {
public:
Time();
time_t getTime();
tm getLocalTime();
tm getGMTTime();
std::string getTimeZone();
std::string toString();
size_t year();
size_t month();
size_t day();
size_t hour();
size_t minutes();
size_t seconds();
private:
std::chrono::time_point<std::chrono::system_clock> tp;
std::tm calTime;
std::string dateTimeSuffix;
};
/*-----------------------------------------------
Construct instance holding time_point for
std::chrono::system_clock's epoch
- epoch is number of seconds since 1 January 1970 UTC
- epoch may vary with clock, e.g.,
system_clock, high_resolution_clock
- time_point is a structure holding chrono::duration
for the clock's epoch
*/
Time::Time() {
tp = std::chrono::system_clock::now();
calTime = getLocalTime();
}
/*-----------------------------------------------
time_t is an integral type holding number of
seconds in the current time_point
*/
std::time_t Time::getTime() {
return std::chrono::system_clock::to_time_t(tp);
}
/*-----------------------------------------------
returns datetime string
- Wed Feb 21 10:18:12 2024 local_time_zone
*/
std::string Time::toString() {
struct tm time;
time_t tt = getTime();
/* compute for GMT zone */
if(dateTimeSuffix == "GMT") {
gmtime_s(&time, &tt);
}
/* compute for local time zone*/
else {
localtime_s(&time, &tt);
}
std::string rs = asctime(&time);
rs.resize(rs.size() - 1); // remove trailing newline
rs += " " + dateTimeSuffix;
return rs;
}
/*-----------------------------------------------
tm is structure holding components of calendar
date and time, e.g., tm_sec, tm_min, ...
- member calTime is localtime after calling
this function
*/
tm Time::getLocalTime() {
time_t tt = getTime();
localtime_s(&calTime, &tt); // save in calTime
dateTimeSuffix = "local time zone";
return calTime;
}
/*-----------------------------------------------
tm is structure holding components of calendar
date and time, e.g., tm_sec, tm_min, ...
- member calTime is gmttime after calling
this function
*/
tm Time::getGMTTime() {
time_t tt = getTime();
gmtime_s(&calTime, &tt); // save in calTime
dateTimeSuffix = "GMT";
return calTime;
}
/*---------------------------------------------
methods to retrieve dateTime components
*/
std::string Time::getTimeZone() {
return dateTimeSuffix;
}
size_t Time::year() {
auto yr = calTime.tm_year + 1900;
return yr;
}
size_t Time::month() {
auto mn = calTime.tm_mon + 1;
return mn;
}
size_t Time::day() {
auto d = calTime.tm_mday;
return d;
}
size_t Time::hour() {
auto hr = calTime.tm_hour;
return hr;
}
size_t Time::minutes() {
auto min = calTime.tm_min;
return min;
}
size_t Time::seconds() {
double sec = calTime.tm_sec;
return sec;
}
}
/*-------------------------------------------------------------------*/
void testtime() {
showNote("test Time","\n");
Time t;
t.getLocalTime();
std::cout << "\n datetime = " << t.toString() << std::endl;
std::cout << "\n epoch in secs = " << t.getTime();
std::cout << "\n year: " << t.year();
std::cout << "\n month: " << t.month();
std::cout << "\n day: " << t.day();
std::cout << "\n hour: " << t.hour();
std::cout << "\n minutes: " << t.minutes();
std::cout << "\n seconds: " << t.seconds();
std::cout << "\n timezone: " << t.getTimeZone();
std::cout << std::endl;
t.getGMTTime();
std::cout << "\n datetime = " << t.toString() << std::endl;
std::cout << "\n epoch in secs = " << t.getTime();
std::cout << "\n year: " << t.year();
std::cout << "\n month: " << t.month();
std::cout << "\n day: " << t.day();
std::cout << "\n hour: " << t.hour();
std::cout << "\n minutes: " << t.minutes();
std::cout << "\n seconds: " << t.seconds();
std::cout << "\n timezone: " << t.getTimeZone();
std::cout << std::endl;
}
--------------------------------------------------
test Time
--------------------------------------------------
datetime = Wed Feb 28 20:35:23 2024 local time zone
epoch in secs = 1709174123
year: 2024
month: 2
day: 28
hour: 20
minutes: 35
seconds: 23
timezone: local time zone
datetime = Thu Feb 29 02:35:23 2024 GMT
epoch in secs = 1709174123
year: 2024
month: 2
day: 29
hour: 2
minutes: 35
seconds: 23
timezone: GMT
/*-------------------------------------------------------------------
Timer provides elapsed time services
*/
class Timer {
public:
Timer();
void start();
void stop();
size_t elapsedNanoSec();
size_t elapsedMicroSec();
size_t elapsedMilliSec();
private:
std::chrono::time_point<
std::chrono::high_resolution_clock
> tp;
std::chrono::time_point<
std::chrono::high_resolution_clock
> starttime;
std::chrono::time_point<
std::chrono::high_resolution_clock
> stoptime;
};
Timer::Timer() {
starttime = std::chrono::high_resolution_clock::now();
stoptime = std::chrono::high_resolution_clock::now();
}
void Timer::start() {
starttime = std::chrono::high_resolution_clock::now();
}
void Timer::stop() {
stoptime = std::chrono::high_resolution_clock::now();
}
size_t Timer::elapsedNanoSec() {
auto duration =
duration_cast<std::chrono::nanoseconds>(stoptime - starttime);
return duration.count();
}
size_t Timer::elapsedMicroSec() {
auto duration =
duration_cast<std::chrono::microseconds>(stoptime - starttime);
return duration.count();
}
size_t Timer::elapsedMilliSec() {
auto duration =
duration_cast<std::chrono::milliseconds>(stoptime - starttime);
return duration.count();
}
/*-------------------------------------------------------------------*/
void testtimer() {
showNote("test Timer");
std::vector<double> v {
1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5
};
/*-------------------------------------------------------
lambda that squares each element of the collection v
*/
auto f = [&v]() {
for(auto &item : v) { item *= item; }
};
/*-------------------------------------------------------
lambda g executes lambda f for n times.
*/
auto g = [f](size_t n) {
for(size_t i = 0; i < n; ++i) { f(); }
};
/*-------------------------------------------------------
Timer test
*/
Timer tmr;
tmr.start();
tmr.stop();
std::cout << "\n noOp elapsed interval in nanosec = " << tmr.elapsedNanoSec();
tmr.start();
g(200);
tmr.stop();
std::cout << "\n g(200) elapsed interval in nanosec = " << tmr.elapsedNanoSec();
std::cout << "\n g(200) elapsed interval in microsec = " << tmr.elapsedMicroSec();
tmr.start();
std::this_thread::sleep_for(std::chrono::milliseconds(5));
tmr.stop();
std::cout << "\n 5 millisec sleep elapsed interval in millisec = " << tmr.elapsedMilliSec();
std::cout << std::endl;
}
--------------------------------------------------
test Timer
--------------------------------------------------
noOp elapsed interval in nanosec = 0
g(200) elapsed interval in nanosec = 2300
g(200) elapsed interval in microsec = 2
5 millisec sleep elapsed interval in millisec = 5
--------------------------------------------------
test Timer
--------------------------------------------------
noOp elapsed interval in nanosec = 200
g(200) elapsed interval in nanosec = 10900
g(200) elapsed interval in microsec = 10
5 millisec sleep elapsed interval in millisec = 12
--------------------------------------------------
test Timer
--------------------------------------------------
noOp elapsed interval in nanosec = 100
g(200) elapsed interval in nanosec = 9600
g(200) elapsed interval in microsec = 9
5 millisec sleep elapsed interval in millisec = 8
--------------------------------------------------
test Timer
--------------------------------------------------
noOp elapsed interval in nanosec = 100
g(200) elapsed interval in nanosec = 7300
g(200) elapsed interval in microsec = 7
5 millisec sleep elapsed interval in millisec = 11
/*---------------------------------------------------------
AnalysisGen.h
- Provides functions that analyze types, display results
and other program defined information.
- Some of this code requires complex template operations.
Those will be discussed in the generics bit.
- You can skip the hard parts until then, without loss
of understanding.
*/
#include <typeinfo> // typeid
#include <utility> // move()
#include <sstream> // stringstream
#include <type_traits> // is_scalar, if constexpr
#include <iostream> // cout
#include <vector> // vector
namespace Analysis {
/*------------------------------------------------------------
Analysis function declarations are provided here so that
definitions below may be placed in any order. That's
needed because C++ requires declaration before use.
*/
template<typename T, int N>
void showArray(std::array<T,N> &a);
template<typename C>
void showColl(const C& c);
template<typename K, typename V>
void showMap(const std::map<K,V> &m);
template<typename T>
void showType(T t, const std::string &nm, const std::string& suffix = "");
void showNote(const std::string& txt, const std::string& suffix = "");
void showOp(const std::string& opstr, const std::string& suffix = "");
void print(const std::string& txt = "");
void println(const std::string& txt = "");
std::string truncate(size_t N, const char* pStr);
std::string indent(size_t n);
template<typename T>
std::string fold(std::vector<T>& v, size_t left, size_t width);
template<typename T>
std::string formatColl(
const T& t, const std::string& nm,
const std::string& suffix = "", size_t left = 2, size_t width = 7
);
template<typename T>
std::string formatScalar(
const T& t, const std::string& nm,
const std::string& suffix = "", size_t left = 2
);
template<typename T>
std::string formatString(
const T& t, const std::string& nm, const std::string& suffix,
size_t left = 2
);
template<typename T>
std::string format(
const T& t, const std::string& nm, const std::string& suffix = "",
size_t left = 2, size_t width = 7
);
/*-- end of function declarations --*/
/*------------------------------------------------------------
Display and Analysis functions and global definitions
--------------------------------------------------------------
*/
const std::string nl = "\n";
/*------------------------------------------------------------
Mutable globals are a common source of bugs. We try not
to use them, but will use DisplayParams here to control how
the insertion operator sends instances to standard output.
*/
struct displayParams {
size_t left = 2; // number of spaces to indent
size_t width = 7; // width of display row
size_t trunc = 40; // replace text after trunc with ...
} DisplayParams; // global object
/*------------------------------------------------------
Demonstration functions
*/
/*-----------------------------------------------
Display calling name, static class, and size
- requires DisplayParams
*/
template<typename T>
void showType(T t, const std::string &callname, const std::string& suffix) {
std::cout << "\n " << callname; // show name at call site
std::cout << " type: "
<< truncate(DisplayParams.trunc,typeid(t).name()); // show type
std::cout << "\n size: " << sizeof(t); // show size on stack
std::cout << suffix;
}
/*-------------------------------------------------------
showArray function -- specific to std::array
*/
template<typename T, int N>
void showArray(std::array<T,N> &a) {
std::cout << " array<T,N> [";
std::cout << a[0];
for(int i=1; i<N; ++i) {
std::cout << ", " << a[i];
}
std::cout << "]" << std::endl;
}
/*-------------------------------------------------------
show sequential collection
- requires integer indexer and size() function
- works for any sequential STL collection
*/
template<typename C>
void showSeqColl(const C& c) {
std::cout << " Collection<T> [";
std::cout << c[0];
for(size_t i=1; i<c.size(); ++i) {
std::cout << ", " << c[i];
}
std::cout << "]" << std::endl;
}
/*-------------------------------------------------------
show associative collection
- requires iterator
- elements must be std::pair<Key, Value>
- works for any associative STL collection
*/
template<typename C>
void showAssocColl(const C& c) {
std::cout << " Collection<K,V> {\n ";
bool first = true;
for(const auto& pair : c) {
if(first) {
std::cout << "{" << pair.first << ", " << pair.second << "}";
first = false;
}
else {
std::cout << ", {" << pair.first << ", " << pair.second << "}";
}
}
std::cout << "\n }\n";
}
/*-----------------------------------------------
Helper function for formatting output
- truncates line to N chars and adds ellipsis
*/
inline std::string truncate(size_t N, const char* pStr) {
std::string temp(pStr);
if(temp.length() > N) {
temp.resize(N);
return temp + "...";
}
return temp;
}
/*-----------------------------------------------
Helper function for formatting output
- generates string of n blanks to offset text
*/
inline std::string indent(size_t n) {
return std::string(n, ' ');
}
/*-----------------------------------------------
Helper function for formatting output
- folds lines after width elements
- used only in Point<T,N>::show()
*/
template<typename T>
std::string fold(std::vector<T>& v, size_t left, size_t width) {
std::stringstream out("\n");
out << indent(left);
for(size_t i=0; i<v.size(); ++i) {
if((i % width) == 0 && i != 0 && i != width - 1) {
out << "\n" << indent(left);
}
if(i < v.size() - 1) {
out << v[i] << ", ";
}
else {
out << v[i] << "\n";
break;
}
}
return out.str();
}
/*-----------------------------------------------
Helper function for formatColl
- defines out << std::pair<K,V>
- used in formatColl for associative containers
*/
template<typename K, typename V>
std::stringstream& operator<<(
std::stringstream& out, const std::pair<K,V>& p
) {
out << "{" << p.first << ", " << p.second << "}";
return out;
}
/*-----------------------------------------------
Format output for Collection types
- any type with iterator, begin(), and end()
like all the STL containers.
- elements need overload for operator<< as
implemented above
- folds into rows with width elements
- will replace folding logic with fold(...)
eventually
*/
template<typename Coll>
std::string formatColl(
const Coll& c, const std::string& nm, const std::string& suffix,
size_t left, size_t width
) {
std::stringstream out;
out << indent(left) << nm << ": {\n" << indent(left + 2);
size_t i = 0;
for(const Coll::value_type& elem : c) {
if((i % width) == 0 && i != 0 && i != width - 1) {
out << "\n" << indent(left + 2);
}
if(i < c.size() - 1) {
out << elem << ", ";
}
else {
out << elem << "\n" << indent(left) << "}" << suffix;
break;
}
++i;
}
return out.str();
}
/*-----------------------------------------------
Format output for scalar types like primitives
*/
template<typename T>
std::string formatScalar(
const T& t, const std::string& nm, const std::string& suffix,
size_t left
) {
std::stringstream out;
out << "\n" << indent(left) << nm << ": " << t << suffix;
return out.str();
}
/*-----------------------------------------------
Format output for strings
- indent and embed in quotation marks
*/
template<typename T>
std::string formatString(
const T& t, const std::string& nm, const std::string& suffix,
size_t left
) {
std::stringstream out;
out << "\n" << indent(left) << nm << ": \"" << t << "\"" << suffix;
return out.str();
}
/*-----------------------------------------------
Defines is_iterable trait
- uses template metaprogramming, e.g., user code
that evaluates during compilation
- detects STL containers and user-defined types
that provide iteration
https://stackoverflow.com/questions/13830158/check-if-a-variable-type-is-iterable
*/
template <typename T, typename = void>
struct is_iterable : std::false_type {};
// this gets used only when we can call
// std::begin() and std::end() on that type
template <typename T>
struct is_iterable<
T,
std::void_t
<decltype(std::begin(std::declval<T>())),
decltype(std::end(std::declval<T>()))>
> : std::true_type {};
template <typename T>
constexpr bool is_iterable_v = is_iterable<T>::value;
/*-----------------------------------------------
Displays almost everything.
- strings work better with formatString(...)
https://www.cppstories.com/2018/03/ifconstexpr/
Iteration is discussed in Bit Cpp_iter
*/
template<typename T>
std::string format(
const T& t, const std::string& nm, const std::string& suffix,
size_t left, size_t width
) {
if constexpr(is_iterable_v<T>) { // decision at compile-time
return formatColl(t, nm, suffix, left, width);
}
else {
return formatScalar(t, nm, suffix, left);
}
}
/*-----------------------------------------------
Display emphasized text
*/
inline void showNote(const std::string& txt, const std::string& suffix) {
print("--------------------------------------------------");
print(" " + txt);
print("--------------------------------------------------");
std::cout << suffix;
}
/*-----------------------------------------------
Display emphasized line
*/
inline void showOp(const std::string& opstr, const std::string& suffix) {
std::cout << "\n --- " << opstr << " ---" << suffix;
}
/*-----------------------------------------------
Display text after newline and indentation
*/
inline void print(const std::string& txt) {
std::cout << "\n " << txt;
}
/*-----------------------------------------------
Display text after newline and indentation
- provides trailing newline
*/
inline void println(const std::string& txt) {
std::cout << "\n " << txt << "\n";
}
}
/*---------------------------------------------------------
Cpp_Generics.cpp
- demonstrates creating and using std::library generic
types: array, basic_string, vector, and map
- demonstrates creating and using user-defined generic
types: HelloTemplates, Stats, and Point
- depends on HelloTemplates.h to provide user-defined
HelloTemplates class
- depends on Stats.h to provide user-defined Stats class
- depends on PointsGen.h to provide user-defined point
class
- depends on Analysis.h for several display and analysis
functions
*/
/*-----------------------------------------------
Note:
Find all Bits code, including this in
https://github.com/JimFawcett/Bits
You can clone the repo from this link.
-----------------------------------------------*/
#include <iostream> // std::cout
#include <memory> // std::unique_ptr
#include <vector> // vector<T> class
#include <array> // array<T> class
#include <map> // map<K,V> class
#include <unordered_map> // unordered_map<k,V> class
#include <set> // set<T> class
#include <thread> // this_thread
#include "AnalysisGen.h" // Analysis functions
#include "HelloTemplates.h" // Stats class declaration
#include "Stats.h"
#include "PointsGen.h" // Point<T, N> class declaration
using namespace Analysis;
using namespace Points;
/*-----------------------------------------------
Overload operator<< for std::vector,
required for demo_std_generic_types()
and testformats()
*/
template<typename T>
std::ostream& operator<<(std::ostream& out, std::vector<T>& v) {
out << format(v, "vector<T>", "", DisplayParams.left, DisplayParams.width);
return out;
}
/*-----------------------------------------------
demonstrate use of std generic types
*/
void demo_std_generic_types() {
/* code elided */
}
/*-----------------------------------------------
demonstrate use of generic functions
*/
void demo_generic_functions() {
/* code elided */
}
int main() {
showNote("Demonstrate C++ Generics", 30, nl);
demo_std_generic_types();
demo_custom_type_HelloTemplates();
demo_custom_type_Stats();
demo_custom_type_Point();
demo_generic_functions();
testtime();
for(size_t i=0; i<4; ++i) {
testtimer();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
print("\n That's all Folks!\n\n");
}
C:\github\JimFawcett\Bits\Cpp\Cpp_Generics\build
> cmake ..
-- The CXX compiler identification is MSVC 19.39.33218.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.39.33218/bin/Hostx64/x64/cl.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.39.33218/bin/Hostx64/x64/cl.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (2.9s)
-- Generating done (0.0s)
-- Build files have been written to: C:/github/JimFawcett/Bits/Cpp/Cpp_Generics/build
C:\github\JimFawcett\Bits\Cpp\Cpp_Generics\build
> cmake --build .
MSBuild version 17.9.0-preview-23551-05+34ae4f308 for .NET Framework
1>Checking Build System
Building Custom Rule C:/github/JimFawcett/Bits/Cpp/Cpp_Generics/CMakeLists.txt
Bits_Generics.cpp
Cpp_Generics.vcxproj -> C:\github\JimFawcett\Bits\Cpp\Cpp_Generics\build\Debug\Cpp_Generics.exe
Building Custom Rule C:/github/JimFawcett/Bits/Cpp/Cpp_Generics/CMakeLists.txt
C:\github\JimFawcett\Bits\Cpp\Cpp_Generics\build
| Reference | Description |
|---|---|
| C++ Story | E-book with thirteen chapters covering most of intermediate C++ |
| C++ Templates | Templates chapter from C++ Story |
| Template Metaprogramming | Template Metaprogramming chapter from C++ Story |
| C++ Bites | Relatively incomplete list of short feature discussions |
| STRCode | User-defined string type with all standard methods and functions documented with: purpose, declaration, definition, invocation, and notes. |
| w3schools tutorial | Slow and easy walk throught basics. |
| cppreference.com | Very complete reference with lots of details and examples. |
Purpose:
template syntax.
⇐ Basic Syntax:
Each generic class and function must be preceeded by atemplate declaration:
that has one or more typename arguments. T is an abstract
type that will be replaced by a specific type in the using
code.
So a template is a pattern for defining one unique class or
function for each unique type list used by an application.
generate two unique classes.
and displays a single instance of the type
almost anything that can send its value to std::cout.
Its special methods are all declared default and no definitions
are provided. That means that the compiler will generate them
by calling the operation applied to a
instance on its composed instance of T.
⇐ Specialization:
Template classes can have a specialized method for a specifiedtype. Specialization is used when a method needs to treat the
specified type differently than for all of the other generic types.
The C++ language guarantees that when the template type
is a specialized type it will call the specialized method. If not a
specialized type the generic method is called.