Showing posts with label Language Support. Show all posts
Showing posts with label Language Support. Show all posts

Friday, July 12, 2024

namespace and using

Overview
In large projects, multiple classes can share same name, which can lead to compilation errors and confusion. namespace can be used to modularise them in separately to avoid overlapping. 
typedefs are commonly used provide an alias to a type. However it has some limitations when used in templates. In C++ 11, using keyword is introduced to overcome these limitations also extend its functionality.

Details
namespace
Consider following code. Here the class circle is defined in two different header files. This leads to compilation errors.
//regions
struct circle{string address();};

//geometry.h
namespace geometry
{
    struct circle{void draw();};
}

//trigonometry.h
namespace trigonometry
{
    struct circle {double area();};
}

    geometry::circle c; //OK
    trigonometry::circle c2; //OK
    ::circle c3;  //OK global namespace
    circle c4; //NOT OK ambiguous scope
scoping is applied using :: in front of namespace. :: without namespace represents global namespace. 
This is depicted in the example 19.

namespaces are open. New types can be added to it. Also alias can be created for namespaces with long names. 
//geometry.h
namespace geometry {struct circle {};}

//sphere.h
namespace geometry  {struct sphere {};}

namespace geo = geometry;   //alias for geometry
geo::circle c;    //geometry::circle
geometry::sphere s;
This is depicted in the example 20.

namespaces can be nested.
namespace shapes
{
    namespace round
    {
        struct circle {};
    }

    namespace rect
    {
        struct square{};
    }
}

using namespace shapes;
round::circle c;  //shapes::round::circle
rect::square s;   //shapes::rect::square
This is depicted in the example 21.

namespaces can be used for  versioning using inline namespaces.
namespace shapes
{
    inline namespace ver3_0
    {
        struct circle {};
    }

    namespace ver2_0
    {
        struct circle {};
    }
    namespace ver1_0
    {
        struct circle {};
    }
} using namespace shapes; circle c; //shapes::ver3_0::circle
This is depicted in the example 22.

nameless namespaces can be used to use types private to a translation unit. Internally compiler adds a unique name to the namespace. Notice that using is not required.
//a.cpp
namespace
{
    struct circle {};
}
circle a;  //becomes QSx5Me43PV402gA::circle

//b.cpp
namespace
{
    struct circle {}; 
}
circle a; //becomes ognslFYviYUnkks::circle


using
namespaces are associated with using declaration and directive. 
using directive
using declaration merges contents of  a namespace into the global namespace. In the following example, all the types of geometry namespace are merged into global namespace. Thus circle c4; declaration is same as geometry::circle c4;
//geometry.h
namespace geometry
{
    struct circle{void draw();};
}

//trigonometry.h
namespace trigonometry
{
    struct circle {double area();};
}
    using namespace geometry;
    geometry::circle c; //OK
    trigonometry::circle c2; //OK
    circle c4; //OK same as geometry::circle
This is depicted in the example 23.

using declaration
Sometimes it could be overwhelming to include entire namespace. In such cases it's possible to include a type as shown below.
    using  std::cout;
    using  std::endl;
    cout << "hello, World!" << endl;

Note that using declaration and directive can be used globally or only to a local scope.
using namespace std; //global scope

//geometry.h
namespace geometry {struct circle {};}

//trigonometry.h
namespace trigonometry  {struct circle {};}

void driver() 
{
    using namespace geometry;    //local to function
    circle c; //geometry::circle
}
This is depicted in the example 24.

using can be used to access overloaded base class functions in the derived class. This is captured in the example below. Here the derived class Y2 retrieves overloaded print function from the base class.
struct X
{
    void print(int){}
};

struct Y: public X
{
    void print(double){}
};

struct Y2: public X
{
    using X::print; //brings base class versions of print
    void print(double){}
};

    Y y;
    y.print(1); //calls print(double)

    Y2 y2;
    y2.print(1); //calls overloaded print(int)
This is depicted in the example 25.

The above can be extended to intializer_list based constructor as well as shown in the example below.
function from the base class.
struct X
{
    X(int) {}
    X(initializer_list<int>) {}
};

struct Y  :public X 
{
};

struct Z  :public X 
{
    using X::X;
};


    Y y{1}; //OK calls X(int)
    Y yy{1,2,3}; //error

   Z z{1}; //OK calls X(int)
   Z zz{1,2,3}; //OK calls X(initializer_list<int>)
This is depicted in the example 26.

using can be used to define type alias in template definitions as well as shown in the  type traits of an  iterator. 
/*
template<typename Iterator>
struct iterator_traits {
using difference_type = typename Iterator::difference_type;
using value_type = typename Iterator::value_type;
using pointer = typename Iterator::pointer;
using reference = typename Iterator::reference;
using iterator_category = typename Iterator::iterator_category;
};
*/

    array<int,10> a;
    auto itr = a.begin();
    //reference to the first element
    iterator_traits<decltype(itr)>::reference b  =  *itr; //int&  
    b = 100; //*itr == 100;
This is depicted in the example 27.

The following example demonstrates declaring alias templates and more.
// type alias, identical to
// typedef std::ios_base::fmtflags flags;
using flags = std::ios_base::fmtflags;
// the name 'flags' now denotes a type:
flags fl = std::ios_base::dec;
 
// type alias, identical to
// typedef void (*func)(int, int);
using func = void (*) (int, int);
 
// the name 'func' now denotes a pointer to function:
void example(int, int) {}
func f = example;
 
// alias template
template<class T>
using ptr = T*;
// the name 'ptr<T>' is now an alias for pointer to T
ptr<int> x;
 
// type alias used to hide a template parameter
template<class CharT>
using mystring = std::basic_string<CharT, std::char_traits<CharT>>;
 
mystring<char> str;
 
// type alias can introduce a member typedef name
template<typename T>
struct Container { using value_type = T; };
 
// which can be used in generic programming
template<typename ContainerT>
void info(const ContainerT& c)
{
    typename ContainerT::value_type T;
    std::cout << "ContainerT is `" << typeid(decltype(c)).name() << "`\n"
                 "value_type is `" << typeid(T).name() << "`\n";
}
 
// type alias used to simplify the syntax of std::enable_if
template<typename T>
using Invoke = typename T::type;
 
template<typename Condition>
using EnableIf = Invoke<std::enable_if<Condition::value>>;
 
template<typename T, typename = EnableIf<std::is_polymorphic<T>>>
int fpoly_only(T) { return 1; }
 
struct S { virtual ~S() {} };
 
    Container<int> c;
   // prints ContainerT is `struct Container<int>`
   // value_type is `int`
    info(c); // Container::value_type will be int in this function
//  fpoly_only(c); // error: enable_if prohibits this
    S s;
    fpoly_only(s); // okay: enable_if allows this


Thursday, May 23, 2024

default and delete

Overview
In  C++ 11, a class can define following special member functions.
  • default constructor
  • copy constructor
  • copy assignment operator
  • destructor
  • move constructor
  • move assignment operator 
If it's missing, the compiler conditionally generates some of these functions.
Sometimes it's desirable to explicitly generate these functions, when it's not generated by the compiler  using keyword =default or suppress conditional generation of these functions by using keyword =delete.

Details
=default
As discussed earlier, following rules apply for the automatic generation of special functions by the compiler.

Default constructor
Generated only if the class contains no user-declared constructors.

Destructor
Generated only if the class contains no user-declared destructor. virtual only if a base class  destructor is virtual.

copy constructor
Performs memberwise copy construction of non-static data members. The copy constructor is generated only for classes lacking an explicitly declared copy constructor, and it’s deleted if a move operation is declared.

copy assignment operator
Performs memberwise copy of non-static data members. The copy assignment operator is generated only for classes lacking an explicitly declared copy assignment operator, and it’s deleted if a move operation is declared. Generation of the copy operations in classes with an explicitly declared destructor is deprecated.

move constructor and move assignment operator
Move operations are generated only for classes lacking explicitly declared move operations, copy operations, and a destructor.

In the following case, the class defines a constructor with a parameter. The compiler throws error if the default constructor is not defined.
struct person
{
    person(string name):name(name){};
    string name;
};

person p; //error no default constructor

Therefore, a default constructor needs to be generated as shown below.
struct person
{
    person(string name):name(name){};
    person()=default;
    string name;
};

person p; //no error
The example 9 depicts this scenario.

=delete
In some cases compiler generated special functions are undesirable. For example, classes such as ios_base suppress use of copy constructor and copy assignment operators by making them private as shown below. This is to prevent instantiated objects to be cloned or copy constructed.
struct person
{
    person(string name):name(name){};
    person()=default;
private:
    person(const person& p)=default;
    person& operator =(const person& p)=default;
    string name;

} p, p2;

    person p3 = p; //error cannot construct
    p2=p; //error cannot clone

instead , they can be simply suppressed by using =delete keyword as shown below.
struct person
{
    person(string name):name(name){};
    person()=default;
    person(const person& p)=delete;
    person& operator =(const person& p)=delete;
    string name;
} p("test"), p2("test2");;

    person p3 = p; //error cannot construct
    p2=p; //error cannot clone

Another case would be to suppress implicit conversions in user defined functions as shown below.
Here passing bool value to function update is prevented.
struct person
{
    person(string name):name(name){};
    person()=default;
    void update (int age){age=age;}
    void update (bool age)=delete;
    person(const person& p)=delete;
    person& operator =(const person& p)=delete;
    string name;
    int age;

} p("test");
p.update(true); //error function deleted
The example 10 depicts this scenario.



Thursday, October 5, 2023

numeric limits

Overview
Maximum and minimum values of integral types such as int, short, long have been defined in <climits>.
Similarly, Maximum and minimum values for float, double are defined in <cfloat>. However these are defined as macros. For example, INT_MAX, INT_MIN, FLT_MIN,FLT_MAX etc.

Details
numeric_limits is a template class that defines functions such as min(), max() along with host of other functions that are related to arithmetic types. 
Specialized classes from numeric_limits are defined for fundamental arithmetic types such as intdouble. These methods in these classes can be used instead of  of macros. 
For example, numeric_limits<int>::max() can be used in place INT_MAX. These classes can be queried for minimum, maximum values and other properties such as number of digits, signed etc. 

The specialized classes are defined as below where xxx is a data types listed below. 
template<> class numeric_limits<xxx>;
Each specialized class will have is_specialized value set to true otherwise it's false.

Integral types
bool char  signed char
unsigned char wchar_t char16_t
char32_t short int unsigned short int
int unsigned int long int
unsigned long int long long int unsigned long long int

Floating point types
float double
long double

The following lists structure of the template class
template <class T> 
class numeric_limits 
{
public:

    /*    Properties    */

//true for all the types specialized from numeric_limits class. static constexpr bool is_specialized = false; //true if the type is signed. static constexpr bool is_signed = false; //true if the type is an integer. static constexpr bool is_integer = false; //true if the type uses exact representations. static constexpr bool is_exact = false; //true if the type has a representation for positive infinity. static constexpr bool has_infinity = false; //true if the type has a representation for a quiet (non-signaling) "Not-a-Number". static constexpr bool has_quiet_NaN = false; //true if the type has a representation for a signaling "Not-a-Number". static constexpr bool has_signaling_NaN = false; /* Denormalized values (representations with a variable number of exponent bits). A type may have any of the following enum values: 1. denorm_absent, if it does not allow denormalized values. 2. denorm_present, if it allows denormalized values. 3. denorm_indeterminate, if indeterminate at compile time. */ static constexpr float_denorm_style has_denorm = denorm_absent; //true if a loss of accuracy is detected as a denormalization loss, rather than an inexact result. static constexpr bool has_denorm_loss = false; /* Rounding style. A type may have any of the following enum values: 1. round_toward_zero, if it rounds toward zero. 2. round_to_nearest, if it rounds to the nearest representable value. 3. round_toward_infinity, if it rounds toward infinity. 4. round_toward_neg_infinity, if it rounds toward negative infinity. 5. round_indeterminate, if the rounding style is indeterminable at compile time. */ static constexpr float_round_style round_style = round_toward_zero; /* true if the type adheres to IEC-559 / IEEE-754 standard. An IEC-559 type always has has_infinity, has_quiet_NaN and has_signaling_NaN set to true; And infinity, quiet_NaN and signaling_NaN return some non-zero value. */ static constexpr bool is_iec559 = false; //true if the set of values represented by the type is finite. static constexpr bool is_bounded = false; /* true if the type is modulo. A type is modulo if it is possible to add two positive numbers and have a result that wraps around to a third number that is less. */ static constexpr bool is_modulo = false; /* For integer types: number of non-sign bits (radix base digits) in the representation. For floating types: number of digits (in radix base) in the mantissa. For example, same as FLT_MANT_DIG for float. */ static constexpr int digits = 0; /* Number of digits (in decimal base) that can be represented without change. For example, same as FLT_DIG for float. */ static constexpr int digits10 = 0; //Number of digits (in decimal base) required to ensure that values that differ are always differentiated. static constexpr int max_digits10 = 0; /* For integer types: base of the representation. For floating types: base of the exponent of the representation (equivalent to FLT_RADIX). */ static constexpr int radix = 0; /* Minimum negative integer value such that radix raised to (min_exponent-1) generates a normalized floating-point number. Equivalent to FLT_MIN_EXP, DBL_MIN_EXP or LDBL_MIN_EXP for floating types. */ static constexpr int min_exponent = 0; /* Minimum negative integer value such that 10 raised to that power generates a normalized floating-point number. Equivalent to FLT_MIN_10_EXP, DBL_MIN_10_EXP or LDBL_MIN_10_EXP for floating types. */ static constexpr int min_exponent10 = 0; /* Maximum integer value such that radix raised to (max_exponent-1) generates a representable finite floating-point number. Equivalent to FLT_MAX_EXP, DBL_MAX_EXP or LDBL_MAX_EXP for floating types. */ static constexpr int max_exponent = 0; /* Maximum integer value such that 10 raised to that power generates a normalized finite floating-point number. Equivalent to FLT_MAX_10_EXP, DBL_MAX_10_EXP or LDBL_MAX_10_EXP for floating types. */ static constexpr int max_exponent10 = 0; //true if trapping is implemented for the type during arithmetic operations. static constexpr bool traps = false; //true if tinyness is detected before rounding. static constexpr bool tinyness_before = false;
    /*    Methods    */

//Minimum finite value based on the type. For example, same as INT_MIN for int. static constexpr T min() noexcept { return T(); } /* Minimum finite value. For integral types: the same as min(). For floating-point types: implementation-dependent; generally, the negative of max(). */ static constexpr T lowest() noexcept { return T(); } //Maximum finite value based on the type. For example, same as INT_MAX for int. static constexpr T max() noexcept { return T(); } /* Machine epsilon (the difference between 1 and the least value greater than 1 that is representable). Equivalent to FLT_EPSILON, DBL_EPSILON or LDBL_EPSILON for floating types. */ static constexpr T epsilon() noexcept { return T(); } //Rqeturns the maximum rounding error of the given floating-point type static constexpr T round_error() noexcept { return T(); } //Returns the positive infinity value of the given floating-point type static constexpr T infinity() noexcept { return T(); } //Returns a quiet NaN value of the given floating-point type static constexpr T quiet_NaN() noexcept { return T(); } //Returns a signaling NaN value of the given floating-point type static constexpr T signaling_NaN() noexcept { return T(); } /* Returns the smallest positive subnormal value of the given floating-point type For types not allowing denormalized values: same as min(). */ static constexpr T denorm_min() noexcept { return T(); } }

Example
/* prints
Minimum value for int: -2147483648
Maximum value for int: 2147483647
int is signed: true
Non-sign bits in int: 31
int has infinity: false
*/
    
  cout << boolalpha;
  cout << "Minimum value for int: " << numeric_limits<int>::min() << endl;
  cout << "Maximum value for int: " << numeric_limits<int>::max() << endl;
  cout << "int is signed: " << numeric_limits<int>::is_signed << endl;
  cout << "Non-sign bits in int: " << numeric_limits<int>::digits << endl;
  cout << "int has infinity: " << numeric_limits<int>::has_infinity << endl;
This is expected in this example.

Included in Header file: <limits>








Saturday, September 30, 2023

constexpr

Overview
Consider following code.  
struct data
{
    const char *name;
    short yob;
    const char *description;
};

auto ntop10 = 10;
array<data, ntop10> top10
{{
    {"Marilyn Monroe",1926 ,"American actress, singer, model"},
    {"Abraham Lincoln",1809 ,"US President during American civil war"},
    {"Nelson Mandela",1918 ,"South African President anti-apartheid campaigner"},
    {"Queen Elizabeth II",1926 ,"British monarch since 1954"},
    {"John F. Kennedy",1917 ,"US President 1961 – 1963"},
    {"Martin Luther King",1929 ,"American civil rights campaigner"},
    {"Winston Churchill",1874 ,"British Prime Minister during WWII"},
    {"Donald Trump",1946 ,"Businessman, US President."},
    {"Bill Gates",1955 ,"American businessman, founder of Microsoft"},
    {"Muhammad Ali",1942 ,"American Boxer and civil rights campaigner"}
}};

double vyasa (double trijya) {return 2.0 * trijya ;}
double kshetra (double trijya) {return trijya * 3.14 * trijya;}
double gems[] {kshetra(10),diaherea(20),kshetra(30),kshetra(40)};
double dias[] {vyasa (10),vyasa (20),vyasa (30),vyasa (40)};
Before constexpr all this was executed at runtime. With constexpr now all these is executed at compile time. Now it's possible that 0 to 100% of code of any existing projects can be optimized during compile time. This is depicted in the example 6
Some examples of constexpr are fixed font generation, object database loading, generating lookup tables etc.

Details
The keyword constexpr means constant expression. Like const, it can be applied to variables: A compiler error is raised when any code attempts to modify the value. Unlike constconstexpr can also be applied to functions and class constructors. constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time.
constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations.

constexpr variables
The primary difference between const and constexpr variables is that the initialization of a const variable can be deferred until run time. A constexpr variable must be initialized at compile time. All constexpr variables are const.
A variable can be declared with constexpr , when it has a literal type and is initialized. If the initialization is performed by a constructor, the constructor must be declared as constexpr.
A reference may be declared as constexpr when both these conditions are met: The referenced object is initialized by a constant expression, and any implicit conversions invoked during initialization are also constant expressions.
constexpr float x = 42.0;
constexpr float y{108};
constexpr float float_array[std::numeric_limits<short>::max()];

It's possible to integral static variables in a class such as int but not float. In such cases constexpr can be used as shown below.
struct s
{
    static const float f=9.0;       //error
    static constexpr float f2=9.0;  //ok
    static const int i=9;           //ok
};

constexpr functions
In a constexpr function, its return value is computable at compile time. The consuming code can use the return value at compile time to initialize a constexpr variable. When its arguments are constexpr values, a constexpr function produces a compile-time constant. When called with regular arguments, or when its value isn't required at compile time, it produces a value at run time like a regular function. Therefore the same function can be reused.
constexpr int factorial(int n)
{
    return n <= 1 ? 1 : (n * factorial(n - 1));
}
//compile time example
static_assert((6 == factorial(3)),"compile time");
This is depicted in the example 7

constexpr constructor
A constructor that is declared with a constexpr  specifier is a constexpr constructor.  It's also bound to restrictions as in case of  constexpr function. 
struct Rectangle
{
    int h, w;
    // constexpr constructor
    constexpr Rectangle(int h, int w) : h(h), w(w) {}
};
//example
constexpr Rectangle r{ 10, 20 };
This is depicted in the example 8


Wednesday, September 27, 2023

initializer_list and list - initialization

Overview
list initialization enables uniform way of initializing scalar and complex objects such as intsdoubles, structs, classes using a pair of braces {}.  A new data type called initializer_list is used for such initialization.

Details
initializer_list
The C++11 standard library introduced a new template based type initializer_list <T> to handle variable-length {}-lists. initializer_list is used to aid uniform initialization. It's automatically created when braces {} are used to initialize.  

The initializer_list  can be also instantiated to define homogeneous lists of varying lengths and passed as arguments. Note that these are not mutable. They can be iterated using iterators. Indexing is not possible.
auto x0 = {}; // error (no element type)
auto x1 = {1}; // initializer_list<int>
auto x2 = {1.,2.}; // initializer_list<double>
auto x3 = {"1","2","3"}; // initializer_list<char*>
auto x4 = {1,2.0}; // error : nonhomogeneous list
*x2.begin()=10; //error 

int high_value(initializer_list<int> val)
{
    int high = numeric_traits<int>lowest();
    if (val.size()==0) 
        return high;

    for (auto x : val)
        if (x>high) 
            high = x;

    return high;
}
int v1 = high_value({1,2,3,4,5,6,7});
int v2 = high_value({-1,2,v1,4,-9,20,v1});

Classes can also provide a constructor accepting initializer_list.  In this case, the compiler will prefers this type of constructor for qualified list construction.
struct  P
{
    int i;
    int j;
    int k;

    P(int, int){}
P(initializer_list<int> lst) { i=j=k=0; auto itr = lst.begin(); if (itr != lst.end()) { i=*itr++; } if (itr != lst.end()) { j=*itr++; } if (itr != lst.end()) { k=*itr++; } } }; P p{1,2,3}; //i=1 j=2 k=3 P p2={4,5}; //i=1 j=2. will not call P(int, int)

list - initialization
eliminates most vexed parsing
mvp happens during  compilation of code such as below. 
The compiler throws an error as it cannot resolve the line highlighted below is a variable declaration or a function declaration. 
TimeKeeper time_keeper(Timer ()); //error
This is depicted in the example 11.

After uniform initialization as shown below, this code compiles without errors.
TimeKeeper time_keeper(Timer {});
This is depicted in the example 12.

eliminates narrowing errors
Enables overflow checks. During compilation, the compiler reports errors for c, i and s for overflow.
char  {0xf0};    //narrowing error
short {0xffff};  //narrowing error
int   {2.0};     //narrowing error

unqualified lists
A unqualified list is used where an expected type is unambiguously known. It can be used as an
expression only as:
  • A function argument
  • A return value
  • The right-hand operand of an assignment operator (=, +=, ∗=, etc.)
  • A subscript
int v {7}; // initializer (direct initialization)
int v2 = {7}; // initializer (copy initialization)
int v3 = m[{2,3}]; // assume m takes value pairs as subscripts
v ={8}; //right-hand operand of assignment
v += {88}; //right-hand operand of assignment
{v} = 9; // error : not left-hand operand of assignment
v = 7+{10}; // error : not an operand of a non-assignment operator
f({10.0}); // function argument
return {11}; // return value

qualified lists
{}-list can be  used as constructor arguments, as if used in a ()-list. List elements are not copied except as by-value constructor arguments.
struct S { int a; double  b; };
S v {7,8.5}; //direct initialization of a variable
v = S{7,8.5}; // assign using qualified list
S* p = new S{7,8.5}; // construct on free store using qualified list

uniform container initialization 
Containers such as vectorlist and  map can be initialized with a list of the objects they are intended to contain.
std::vector<int>          {1,2,3,4,5};
std::vector<int>          {1,2.0,3,4,5}; //error 2.0 is not int
std::list<std::string>    {"mon","tue","wed","thu","fri","sat","sun"};
std::map<std::string,int> {{"a",97},{"b",98},{"c",99}};

//internally compilers generates code for the assignment
//for example vector<double> v = {1, 2, 3.14};
//becomes similar to
//const double temp[] = {double{1}, double{2}, 3.14 } ;
//const initializer_list<double> tmp(temp,sizeof(temp)/sizeof(double));
//vector<double> v(tmp);

usages examples
In the example 13 below and as seen in its console output. the compiler initializes the data members of the  POD object based on their position in the class.

In the example 14 below and seen in its console output. the compiler calls different constructors of POD based on the data types.

In the example 15 below and its console output. the POD is constructed using  an initializer_list constructor.

Side effects 
The STL container classes such as vector provide list initialization constructors. However it can introduce unwanted side effects as shown in the example 16 and its console output below. Therefore parenthesis () based constructors should be used where needed.