Question

Does someone know of any uses of m4 besides autoconf (preferably in a c or c++ environment) that is more than just an academic excerise, because it helped solve a problem that would otherwise (for example with a 'standard' preprocessor) have been difficult to solve.

I ask this because I am thinking about a way to do a project in c++ where I want to reduce or minimize redundancies by using scripts, preprocessors, whatevers.

Was it helpful?

Solution

I have once used m4 to generate a C++ header file from a text file containing a simple version string (MAJOR.MINOR.MICRO-STATUS), and information from subversion, for a Windows Visual Studio project.

m4 was the smallest macro engine that I could easily embed and use for this specific goal.

It looked like this:

#ifndef __VERSION_H__
#define __VERSION_H__
divert(-1)
define(`CPP_DEFINE', `#define $1 $2')
define(`VERSION', include(`version.txt'))
define(`MY_SOFTWARE_MAJOR', regexp(VERSION, `\([0-9]+\)\.[0-9]+\.[0-9]+', `\1'))
define(`MY_SOFTWARE_MINOR', regexp(VERSION, `[0-9]+\.\([0-9]\)+\.[0-9]+', `\1'))
define(`MY_SOFTWARE_MICRO', regexp(VERSION, `[0-9]+\.[0-9]+\.\([0-9]\)+', `\1'))
define(`MY_SOFTWARE_STATUS', regexp(VERSION, `\(-\w+\)', `\1'))
define(`SVN_REV', `regexp(esyscmd(svnversion -n), `[0-9]+', `\&')')
ifelse(len(SVN_REV), 0, `define(`NO_SVN')')
divert
CPP_DEFINE(MY_SOFTWARE_VERSION, format(`"%s.%s.%s"', MY_SOFTWARE_MAJOR, MY_SOFTWARE_MINOR, MY_SOFTWARE_MICRO))
CPP_DEFINE(PRODUCT_VERSION, format(`"%s.%s.%s%s"', MY_SOFTWARE_MAJOR, MY_SOFTWARE_MINOR, MY_SOFTWARE_MICRO, MY_SOFTWARE_STATUS))
CPP_DEFINE(COPYRIGHT_NOTICE, `"Copyright (C) 2008 - Me"')
ifdef(`NO_SVN', `
CPP_DEFINE(ABOUT_VERSION, format(`"My Software Version %s.%s.%s%s"', MY_SOFTWARE_MAJOR, MY_SOFTWARE_MINOR, MY_SOFTWARE_MICRO, MY_SOFTWARE_STATUS))
CPP_DEFINE(FILE_VERSION, format(`"%s.%s.%s"', MY_SOFTWARE_MAJOR, MY_SOFTWARE_MINOR, MY_SOFTWARE_MICRO))
CPP_DEFINE(INFO_VERSION, format(``%s,%s,%s,0'', MY_SOFTWARE_MAJOR, MY_SOFTWARE_MINOR, MY_SOFTWARE_MICRO))
', `
CPP_DEFINE(ABOUT_VERSION, format(`"My Software Version %s.%s.%s.%s%s"', MY_SOFTWARE_MAJOR, MY_SOFTWARE_MINOR, MY_SOFTWARE_MICRO, SVN_REV, MY_SOFTWARE_STATUS))
CPP_DEFINE(FILE_VERSION, format(`"%s.%s.%s.%s"', MY_SOFTWARE_MAJOR, MY_SOFTWARE_MINOR, MY_SOFTWARE_MICRO, SVN_REV))
CPP_DEFINE(INFO_VERSION, format(``%s,%s,%s,%s'', MY_SOFTWARE_MAJOR, MY_SOFTWARE_MINOR, MY_SOFTWARE_MICRO, SVN_REV))
')
#endif /* __VERSION_H__ */

Although it worked perfectly, this really was an experiment that I did not reiterate, mainly because I now prefer using CMake's builtin capabilities to handle that stuff directly and generate my Visual Studio project files.

OTHER TIPS

GNU Bison uses it internally, to generate the C or C++ parser files.

I once used it to generate pieces of a fiendish SQL query. Printed, the query ran to almost 20 pages--roughly 1200 lines, I think. I'm pretty sure I couldn't have done it without m4, and I'm thankful I won't have to do it ever again.


(Responding to a comment . . .)

IIRC (from 30 years ago), the query drove a report of plaintiffs. Plaintiffs were categorized by combinations of events their history.

The SQL engine couldn't build a view on a view, and didn't support user-defined functions. Some long expressions for derived tables, subselects, etc., either

  • should have been identical, but were not, or
  • should have been subtly different (needed '-' instead of '+', or 'shsapnm' instead of 'shsgpnm'), but were different in the wrong way.

The hard part wasn't the programming.

The first hard part was figuring out which of, say, three slightly different subselects was the right one, or whether we really needed three different ones, or whether we really needed the existing three plus one or two more. (I think the answer eventually turned out to be "all of the above".) Related reports needed some of the same subselects; m4 + make guaranteed they were the same.

The second hard part was building test data that could show the query was working right in the face of all the various, complex plaintiff histories. I think Joe Celko steered me toward some commercial software Boeing was using at the time--it took some user-defined criteria and and generated a test harness and stubs for every possible combination or permutation. I don't remember its name. (I think this is it: Logic Gem)

I've used m4 and was impressed by its capabilities, this what C preprocessor should be. I used it to generate GNU makefiles from simpler project descriptions.

Sendmail provides an M4-based infrastructure for generating configuration files. Most sendmail installs I have worked with provide this as the recommended method of configuration sendmail.

I've used m4 to generate C/C++ files that contain lists of struct declarations. Depending on how ugly the struct is and if you do it right, the m4 files can be easier to read and edit than the C/C++ files.

The official C++ binding library for GTK+, gtkmm - an excellent project that makes the power and reach of the GTK+ GUI accessible via ever-more-modern C++ - uses m4 as part of its toolset for easily generating wrapping code.

(I have no affiliation with any G* project. I just like gtkmm a lot and feel it's not as well-known as it could/should be, especially versus certain obvious alternatives. Also, I had no idea m4 was K&R until I researched it for this! Neat.)

I have used M4 to automatically generate DDL scripts to define your SQL tables, the C++ code and headers to access them, and the drivers to test this. I think we also updated the scripts to backup and re-populate the tables at the same time, but it has been 15+ years since I did this.

I used both cpp and m4 to replace tokens in files and to generate packaging files for different target platforms. now I'll use ruby erb to do this. and while at sun I used m4 for preprocessing header files

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top