Showing posts with label c++. Show all posts
Showing posts with label c++. Show all posts

Saturday, March 13, 2021

C Storage Classes




In investigating the proper means to declare a global variable, I found myself reviewing some C documentation that I had long forgotten, concerning the Storage Class.

To briefly review, each variable declaration consists of 3 elements: the storage class, the type, and the variable name. For example:


auto int x;
static int y;


The storage class can take the form of auto, extern, static, or register. If not specified, the implicit storage class is that of auto.

The auto storage class is the most frequently used of the classes, primarily because it is the implicit default. Local variables take this form, where the storage is not allocated until the block in which the variable is defined is entered.

The extern storage class specified simply a reference to a variable that is defined elsewhere. Space is therefore not allocated upon encountering this reference, since the storage is allocated elsewhere. This as you may recall is the means to declare a global variable.

The static storage class specifies that the variable cannot be access by functions outside the translation unit in which it was defined. A common error is declaration of a static variable in a header file, which is imported by more than one translation unit not understand that each translation unit essentially created independent copies of the variable. A common practice of declaring a static constant in a header, used by multiple translation units results in multiple copies of the constant, in each translation unit. However, if the static variable simply defines a constant....generally, no-harm-no-foul. It is however worth understanding that it is not a shared reference.

The last storage class is that of register, which notifies the compiler to make the variable as efficient as possible. Ideally, the variable will retain it's location in a cpu register for optimal performance.

Sunday, March 22, 2020

Command Line Processing with Popt

General practice for writing flexible applications lend toward the ability to specify command-line arguments to tailor the behavior of your application. For instance, a common practice of specifying -v to enable a verbose mode where debugging information is echoed as your application runs. Specifying a unique port for networking applications, or enablement/disablement of an application gui are two other examples that are common. Since command line arguments can take the flavor of integer, floats, doubles, strings...the code for parsing these arguments can be far from simple. Tailoring flexible parsers every time you develop an application is labor-intensive and a waste of time if a general mechanism were available.

Lucky for us, such a mechanism does exist; namely the Popt library.

Start by installing the Popt package:
$ sudo apt-get install libpopt-dev

A short example of an application which uses the library follows:

 1  #include <popt.h>
 2  #include <stdio.h>
 3  #include <stdlib.h>
 4
 5  int main(int argc, char **argv) {
 6          /* option parsing variables */
 7          char ch;
 8          poptContext opt_con;   /* context for parsing command-line options */
 9          char *extra_arg;
10          static int i=0;
11          static char *s="";
12          static float f=0.0;
13          static double d=0.0;
14          int verbose=0;
15
16          static struct poptOption options_table[] = {
17                  { "integer", 'i', POPT_ARG_INT, &i, 'i', "grab an integer", "INT" },
18                  { "string", 's', POPT_ARG_STRING, &s, 's', "grab a string", "STRING" },
19                  { "float", 'f', POPT_ARG_FLOAT, &f, 'f', "grab a float", "FLOAT" },
20                  { "double", 'd', POPT_ARG_DOUBLE, &d, 'd', "grab a double", "DOUBLE" },
21                  { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "enable verbose", "" },
22                  POPT_AUTOHELP
23                  { NULL, 0, 0, NULL, 0 } /* end-of-list terminator */
24          };
25
26          opt_con = poptGetContext(NULL, argc, (const char **)argv, options_table, 0);
27
28          /* Now do options processing */
29          while ((ch = poptGetNextOpt(opt_con)) >= 0) {
30                  printf("between while & switch: ch = %c\n", ch);
31                  switch (ch) {
32                          case 'i':
33                                  printf("handling 'i' option.\n");
34                                  break;
35                          case 's':
36                                  printf("handling 's' option.\n");
37                                  break;
38                          case 'f':
39                                  printf("handling 'f' option.\n");
40                                  break;
41                          case 'd':
42                                  printf("handling 'd' option.\n");
43                                  break;
44                          case 'v':
45                                  printf("handling 'v' option.\n");
46                                  verbose = 1;
47                                  break;
48                  }
49          }
50
51          if (ch < -1) {   

52                  // the user specified an invalid option, tell them   
53                  poptPrintHelp(opt_con, stderr, 0);       
54          }       
55       
56          /* non-option args */       
57          while (extra_arg = (char *)poptGetArg(opt_con)) {       
58                  printf("extra arg: %s\n", extra_arg);       
59                  exit(1);       
60          }       
61       
62       
63          /* cleanup */       
64          poptFreeContext(opt_con);       
65       
66  printf("(%s:%d) i = %d\n",__FILE__,__LINE__,i);       
67  printf("(%s:%d) s = '%s'\n",__FILE__,__LINE__,s);       
68  printf("(%s:%d) f = %f\n",__FILE__,__LINE__,f);       
69  printf("(%s:%d) d = %lf\n",__FILE__,__LINE__,d);       
70  printf("(%s:%d) v = %d\n",__FILE__,__LINE__,verbose);       
71       
72       
73    return EXIT_SUCCESS;       
74  }


You compile and link the application by:
g++ -Wall   main.o  -lpopt -o main


An added bonus of using this application is the automatic introduced interfaces for a brief and more verbose help descriptions.


~/Desktop/sourceCode/C/popt$ ./main --help
Usage: main [OPTION...]
-i, --integer=INT       grab an integer
-s, --string=STRING     grab a string
-f, --float=FLOAT       grab a float
-d, --double=DOUBLE     grab a double
-v, --verbose           enable verbose

Help options:
-?, --help              Show this help message
--usage                 Display brief usage message



~/Desktop/sourceCode/C/popt$ ./main --usage
Usage: main [-v?] [-i|--integer INT] [-s|--string STRING] [-f|--float FLOAT]
      [-d|--double DOUBLE] [-v|--verbose] [-?|--help] [--usage]



The heart of the library lies with proper initialization of the popt structure which is defined as follows:

struct poptOption {
  const char * longName;  
  char shortName;        
  int argInfo;
  void * arg;            
  int val;                
  const char * descrip;  
  const char * argDescrip;
};

The ability to specify either a long or short argument name is common practice; -h or --help is a common example of this form.

The long and short forms are specified as the 1st and 2nd element in the popt structure. The 3rd and 4th specify of what type the following argument consists of as well as the address in which the value will be stored. The 6th and 7th arguments specify the argument description and argument field name which is displayed in the help and brief. The 5th field is a bit of a mystery at this time.

That's all for now.

Sunday, February 23, 2020

Gprof Profiling


Profiling is a mechanism for identifying program 'hotspots', identifying where your program spends a good majority of it's time. These regions are prime candidates for optimizations, where concentration of efficiency will give you the most bang for your buck.

Profiling with Gprof consists of 3-phases: 1) compiling your application with profiling enabled, 2) executing the application to gather profiling metrics, and 3) evaluating the collected metrics.

Compiling

You first must compile your application similar to the way you normally compile it. Two additional flags must be specified however, the -pg option to enable profiling, and the -g option to introduce the debugging symbols for tracking source lines of code. Actually, the -g option is only necessary for line-by-line profiling, but for good measure I suggest specifying it regardless.

$ gcc -pg -g foo.c -o foo


Executing

You run your application in the same manner as your normally run it, specifying the same arguments, inputs, outputs, . . . What you may notice however is that the program will execute slower than normal. This is reasonable when you consider what is taking place. Profiling metrics are collected during the execution of the program. Worth noting, your executable needs to terminate in a normal fashion, by returning from the main routine or calling exit().
Immediately prior to terminating the program, an output file (gmon.out) is generated that contains the collected profiling metrics. This file will later be used in evaluating the program performance.

Evaluation

Evaluation of the results stored in the gmon.out file is the last step in our quest. Various types of reports are available from the collected profiler info, the most readily used is the flat model. This is done by executing the command:

$ gprof foo


This results in the default report, a flat model followed by the call graph. The flat model will depict the list of functions in decreasing order with respect to time spent in each function. This allows for quick evaluation of the functions that your application spends most it's time in. You may notice that two functions appear in every profile; mcount and profil, each function is part of the profiling apparatus. The time spent in both functions can be viewed as profiler overhead.


Sunday, January 19, 2020

Formal Names For Common C Techniques

If you ever written a C application with custom-defined header files, you've used the pre-processor pattern:


#ifndef FOO_H
#define FOO_H
.
.
.
#endif


Ever wonder what this pattern was called? I never did, until the other day when I searched for it. The pattern is referred to as the 'once-only header' pattern, sometimes called the 'wrapper #ifdef'.

Call it what you want, the intention is to ensure that the header is not copy-n-pasted multiple times in a translation unit, due to multiple direct or indirect includes. The FOO_H is referred to as the 'guard' or 'controlling macro'.

Another common technique that you may have made use of goes something like this:

#ifdef SYS1
#include "sys1.h"
#elif . . .
.
.
.
#endif

This is referred to as a 'computed includes' pattern or technique. Not much else to add, just a name for a common face.

Thursday, January 2, 2020

Global Variables in C

First the disclaimers;
Global variables are bad. Global variables are evil. Global variables are, and will continue to be much like a 6-ft chainsaw, extremely dangerous to use, can regularly be replaced with a safer tool/technique, but are sometimes the proper tool for the job (like cutting down 12-ft diameter trees). None-the-less, on the rare occasion that a global variable is the proper technique, you'll need to know how to define and use them...the subject of the remainder of this post.

The following example will be used to explain techniques for defining and using globals, we will speak to it for the remainder of the post.

package1.h


1 #ifndef PACKAGE1_H
2 #define PACKAGE1_H
3 static const float GlobFloat = 3.14159;
4 static double GlobDouble = 1.23456;
5 extern unsigned GlobUnsigned;
6 //char GlobChar='a';
7
8 void func1(void);
9 #endif

package1.c


1 #include "package1.h"
2 #include
3
4 static int PackageGlobalInt = 1;
5 unsigned GlobUnsigned = 12345;
6
7 void func1() {
8 printf("(%s:%d) PackageGlobalInt(%d)\n",__FILE__,__LINE__,PackageGlobalInt++);
9 printf("(%s:%d) GlobFloat(%f)\n",__FILE__,__LINE__,GlobFloat);
10 printf("(%s:%d) GlobDouble(%lf)\n",__FILE__,__LINE__,GlobDouble++);
11 printf("(%s:%d) GlobUnsigned(%u)\n",__FILE__,__LINE__,GlobUnsigned++);
12 printf("(%s:%d) GlobDouble(%lf)\n",__FILE__,__LINE__,GlobDouble++);
13 printf("(%s:%d) GlobUnsigned(%u)\n",__FILE__,__LINE__,GlobUnsigned++);
14 }
15

package2.h


1 #ifndef PACKAGE2_H
2 #define PACKAGE2_H
3 #include "package1.h"
4
5 extern long GlobLong;
6 void func2(void);
7 #endif


package2.c


1 #include "package2.h"
2 #include
3
4 static int PackageGlobalInt = 2;
5 long GlobLong = 987654321;
6
7 void func2() {
8 printf("(%s:%d) PackageGlobalInt(%d)\n",__FILE__,__LINE__,PackageGlobalInt++);
9 printf("(%s:%d) GlobFloat(%f)\n",__FILE__,__LINE__,GlobFloat);
10 printf("(%s:%d) GlobDouble(%lf)\n",__FILE__,__LINE__,GlobDouble++);
11 printf("(%s:%d) GlobUnsigned(%u)\n",__FILE__,__LINE__,GlobUnsigned++);
12 printf("(%s:%d) GlobLong(%li)\n",__FILE__,__LINE__,GlobLong++);
13 printf("(%s:%d) GlobDouble(%lf)\n",__FILE__,__LINE__,GlobDouble++);
14 printf("(%s:%d) GlobUnsigned(%u)\n",__FILE__,__LINE__,GlobUnsigned++);
15 printf("(%s:%d) GlobLong(%li)\n",__FILE__,__LINE__,GlobLong++);
16 }
17


main.c


1 #include
2 #include "package1.h"
3 #include "package2.h"
4
5 int main() {
6 printf("(%s:%d) main process initializing\n",__FILE__,__LINE__);
7
8 func1();
9 func2();
10
11 printf("(%s:%d) GlobFloat(%f)\n",__FILE__,__LINE__,GlobFloat);
12 printf("(%s:%d) GlobDouble(%lf)\n",__FILE__,__LINE__,GlobDouble);
13 printf("(%s:%d) GlobUnsigned(%u)\n",__FILE__,__LINE__,GlobUnsigned);
14 printf("(%s:%d) GlobLong(%li)\n",__FILE__,__LINE__,GlobLong);
15
16
17 printf("(%s:%d) main process terminating\n",__FILE__,__LINE__);
18 }
19


Globals are a form of interface, so rightly are typically defined in a header file. Package 1 attempts to define 3 global variables. You will soon see that it was only successful in defining one. Before we get into the details, we should review some preliminaries.

#includes

As you should already be aware, header files serve little purpose other than organization of code. When encountering a #include directive, the precompiler does little more than copy-n-pasting the contents into the sourcing file. This is significant in our later discussion.

Static Variables

Static variables cannot be accessed outside a translation unit. For all intents and purposes, a translation unit is a C source file. This, along with what we know about #includes will explain why only one of our intended global variable declarations was successful.

Back To Our Example

The contents of package1.h on lines 3-5 are three attempts to declare a global variable. Note, lines 3-4 define the variables as static, line 5 defining it as external linkage. Why are the first two variables defined as static??? Because if I don't, I get a linker error "multiple definition of . . ." error. Pounding on the keyboard, slowly beating the linker into submission is accomplished by declaring the variable as static. Finally, the linker shuts up; we must have fixed it, right? Wrong!

So, why doesn't lines 3-4 work? More importantly, how do we know it doesn't work? Well, we can confirm that line 4 didn't work by comparing the results of lines package1.c:10, package1.c:12, package2.c:10, package2.c:13, and main.c:12. Note that on package 1 & 2 functions, we are incrementing the contents of GlobDouble. If we were successful in defining a global variable, we should see the variable initially assigned 1.23456, incremented 4 times with a final result of 5.23456.


~/StorageClasses$ ./main | grep GlobDouble
(package1.c:10) GlobDouble(1.234560)
(package1.c:12) GlobDouble(2.234560)
(package2.c:10) GlobDouble(1.234560)
(package2.c:13) GlobDouble(2.234560)
(main.c:12) GlobDouble(1.234560)

Hmmmph, we didn't accomplish what we intended, did we? The static storage class, coupled with the precompiler copy-n-paste behavior resulted in 3 independent variable declarations; one in package1.c, one in package2.c, and one in main.c. Each instance is unique to each translation unit, so incrementing one only affects the declaration in that translation unit.

Line 5 however successfully defined a global variable. How? Well, line 5 simply states that we are declaring a variable, to be defined elsewhere. Declaration simply defines a reference to variable that will be defined elsewhere (in this case, line 5 in package1.c). Translation units: package1.c, package2.c and main.c will each declare a reference to GlobUnsigned, but the single definition of the variable resides in package1.c.

We can confirm that all is well by means of:


~/StorageClasses$ ./main | grep GlobUnsigned
(package1.c:11) GlobUnsigned(12345)
(package1.c:13) GlobUnsigned(12346)
(package2.c:11) GlobUnsigned(12347)
(package2.c:14) GlobUnsigned(12348)
(main.c:13) GlobUnsigned(12349)



It is worth noting that lines 3-4 in package1.h have identical results as line 4 in both package1.c and package2.c. All instances are similar in the matter that their scope is limited to the translation unit in which they are declared.

Monday, November 21, 2016

C Storage Classes -- Where Do You Store Your Foo?

In investigating the proper means to declare a global variable, I found myself reviewing some C documentation that I had long forgotten, concerning the Storage Class.

To briefly review, each variable declaration consists of 3 elements: the storage class, the type, and the variable name. For example:


auto int x;
static int y;


The storage class can take the form of auto, extern, static, or register. If not specified, the implicit storage class is that of auto.

The auto storage class is the most frequently used of the classes, primarily because it is the implicit default. Local variables take this form, where the storage is not allocated until the block in which the variable is defined is entered.

The extern storage class specified simply a reference to a variable that is defined elsewhere. Space is therefore not allocated upon encountering this reference, since the storage is allocated elsewhere. This as you may recall is the means to declare a global variable.

The static storage class specifies that the variable cannot be access by functions outside the translation unit in which it was defined. A common error is declaration of a static variable in a header file, which is imported by more than one translation unit not understand that each translation unit essentially created independent copies of the variable. A common practice of declaring a static constant in a header, used by multiple translation units results in multiple copies of the constant, in each translation unit. However, if the static variable simply defines a constant....generally, no-harm-no-foul. It is however worth understanding that it is not a shared reference.

The last storage class is that of register, which notifies the compiler to make the variable as efficient as possible. Ideally, the variable will retain it's location in a cpu register for optimal performance.

Monday, September 26, 2016

Logging to SysLog

Trace logging of significant events is a fundamental method for debugging and isolating errors encountered. User warnings, pop-up notification boxes, and application-level log files are one way to go. Another; to make use of the standard system log services found on *nix systems. The remainder of this post will some brief information I've recently discovered concerning interfacing with the system log services.

The syslog system import defines the interface:


   extern void syslog (int __pri, __const char *__fmt, ...)


which allows for variable-lengthed argument lists, much like printf. The interface is pretty simple, the first argument specifies a priority or type of log statement; the second, a formatted string in the same form that printf takes.

A simple C

#include <stdio.h>
#include <syslog.h>

int main(int argc, char *argv[]) {
  syslog(LOG_NOTICE,"%s process started", argv[0]);
  sleep(3);
  syslog(LOG_NOTICE,"%s process terminating", argv[0]);
}


Compile and running this example will result in two log entries in the system logs. Determining what log will contain the log statements can be determined by:
1) the log type, and
2) the /etc/syslog.conf file.
Note, that the log type is defined as LOG_NOTICE, and the entry in the syslog.conf file entry;

  *.=info;*.=notice;*.=warn;\
 auth,authpriv.none;\
 cron,daemon.none;\
 mail,news.none          -/var/log/messages

shows that the notice log entries will be evident in /var/log/messages.

Since the files are only accessible to superusers, logging in as root and tailing the log file should show the messages in the same manner as:

# cat /var/log/syslog
.
.
.
Jul  5 20:30:18 riffraff syslogd 1.4.1#18: restart.
Jul  5 20:42:07 riffraff app: ./app process started
Jul  5 20:42:10 riffraff app: ./app process terminating


You can interface with the syslog service using scripts as well.

$ logger -p user.notice "hello again"
# cat /var/log/syslog
.
.
.
Jul  5 20:42:07 riffraff app: ./app process started
Jul  5 20:42:10 riffraff app: ./app process terminating
Jul  5 21:04:06 riffraff lipeltgm: hello again


Pretty slick, huh?