Red Hat Enterprise Linux 8 introduces Application Streams. Software Collections (SCLs) are no longer needed in RHEL 8. According to Introducing Application Streams in RHEL 8, Application Streams, containerization, or virtualization can be used to fulfill needs met previously by SCLs.
I recently updated PHP and Apache httpd on a CentOS 6 server with Software Collections. Software Collections allowed installation of multiple software versions. Software depending on older versions of other software can continue functioning, while other software requiring new versions of that other software can be installed and used. SCLs helped me gradually transition my PHP applications from httpd+mod_php to httpd+php-fpm.
With anticipation of CentOS 8 soon being available through my virtual server provider, GoDaddy, I wanted to become familiar with RHEL 8. I wanted to deploy CentOS 8 for my next virtual server, and I wanted the server to use httpd and php via SCL only. I am now rethinking my plan to use SCL on CentOS 8 to allow future transitioning of applications toward newer software versions at a pace I control.
Working within a software development team is like being a member of a jazz band. Software architects convert specifications into overall designs. Engineers distill overall designs into detailed designs of individual modules. And, implementers convert detailed designs into source code. Each role has some degree of creative freedom. Each member improvises while keeping their contributions consistent with the whole software system.
In La La Land, Sebastian speaking about individual musicians of a jazz band captures the sentiment exactly:
“One after the other, everyone gets their moment… And you put it all together — each player, each sound — into one single story.” (La La Land)
La La Land. Dir. Damien Chazelle. Lionsgate, 2016. Film.
I have been partly responsible for maintaining legacy source code. Almost all of the code is implemented in C and therefore does not use exceptions. Much of the error handling code follows the following pattern here implemented in C++ (imagine in this example, operator new returns 0 when no memory is available):
int function()
{
MyTypeA * ptrValTypeA = 0;
MyTypeB * ptrValTypeB = 0;
ptrValTypeA = new MyTypeA();
if(ptrValTypeA == 0)
{
/* error handling #1 */
return -1; /* error, no memory */
}
ptrValTypeB = new MyTypeB();
if(ptrValTypeB == 0)
{
/* error handling #2 */
delete ptrValTypeA;
return -1; /* error, no memory */
}
/* ... more code ... */
if(function_call() == -1)
{
/* error handling #3 */
delete ptrValTypeA;
delete ptrValTypeB;
return -1;
}
/* ... even more code ... */
return 0; /* no error */
}
The above code could be reduced to the following (assuming the default behavior of operator new throwing an exception when memory is exhausted):
int function()
{
MyTypeA * ptrValTypeA = 0;
MyTypeB * ptrValTypeB = 0;
try
{
ptrValTypeA = new MyTypeA();
ptrValTypeB = new MyTypeB();
/* ... more code ... */
if(function_call() == -1)
throw Exception();
/* ... even more code ... */
}
catch(...)
{
/* error handling */
delete ptrValTypeA;
delete ptrValTypeB;
return -1;
}
return 0; /* no error */
}
Error handling in the first code snippet is spread to multiple places in the code. This makes the code less readable and therefore less maintainable.
The second code snippet shows that error handling is localized. The normal flow of the code is easy to follow, and freeing allocated resources is performed in one place when an exception occurs.
I am currently tasked with designing and implementing a C# software component. There are software developers who insist on adopting error return codes over exceptions because of unmeasured performance concerns. For example, some developers demand database connection timeouts or failures should use error return codes. My experience with error handling in legacy C code makes me resist error return codes and prefer exceptions.
If exceptions are used properly for exceptional cases such as an offline database, then the performance impact will be rarely realized and the code can be cleanly written. A performance hit that only happens during unexpected or abnormal operation is a good trade for clean code without the cost of constant error checking during normal operation.
For further reading, see Why Exceptions Should Be Exceptional.
I inherited the responsibility of maintaining an internal development tool implemented in C#. I recently added new classes to extend the tool’s functionality. Several of the new classes uses several properties, and I found myself writing code similar to the following:
class Shape
{
private int _width;
private int _height;
private bool _isVisible;
public int Width {
get {
return _width;
}
}
public int Height {
get {
return _height;
}
}
public bool IsVisible {
get {
return _isVisible;
}
set {
_isVisible = value;
}
}
/* ... more code, including fields,
properties, and functions ... */
}
Implementation of properties as is done in the above source code can fill whole screens. I found a concise way to write the above code while browsing references, and I present it here:
class Shape
{
public int Width { get; }
public int Height { get; }
public bool IsVisible { get; set; }
/* ... more code, including fields,
properties, and functions ... */
}
According to Microsoft’s C# Programming Guide1, with auto-implemented properties, “the compiler creates a private, anonymous backing field that can only be accessed through the property’s get and set accessors.” Separate fields for auto-implemented properties are not used because of private, anonymous backing fields. Setting property values for auto-implemented properties without set accessors can be done by providing an initialization value when creating the auto-implemented property or by assigning a value to the auto-implemented property within the constructor. A reduction of lines is evident in the above source code.
Interested in keeping up with the latest C++ standard, I noticed that CentOS 7.4.1708 uses gcc version 4.8.5 20150623. This version of gcc defaults to ISO/IEC 14882:1998. Updated C++ standards have been released in 2003, 2011, 2014, and 2017. Although gcc developers are still implementing the 2017 standard, it is good to use a recent version of gcc that defaults to the 2014 standard. Installing centos-release-scl and devtoolset-7-gcc-c++ packages allows me to use a recent version of gcc.
To determine the default C++ standard used by g++:
1. $ g++ -dM -E -x c++ /dev/null | \
grep -F __cplusplus
I did the following to install devtoolset-7:
1. # yum install centos-release-scl
2. # yum install devtoolset-7-gcc-c++
Every time I want gcc to default to using GCC 7, I run the following:
1. $ scl enable devtoolset-7 bash