Can gcc/clang optimize initialization computing? - c

I recently wrote a parser generator tool that takes a BNF grammar (as a string) and a set of actions (as a function pointer array) and output a parser (= a state automaton, allocated on the heap). I then use another function to use that parser on my input data and generates a abstract syntax tree.
In the initial parser generation, there is quite a lot of steps, and i was wondering if gcc or clang are able to optimize this, given constant inputs to the parser generation function (and never using the pointers values, only dereferencing them) ? Is is possible to run the function at compile time, and embed the result (aka, the allocated memory) in the executable ?
(obviously, that would be using link time optimization, since the compiler would need to be able to check that the whole function does indeed have the same result with the same parameters)

What you could do in this case is have code that generates code.
Have your initial parser generator as a separate piece of code that runs independently. The output of this code would be a header file containing a set of variable definitions initialized to the proper values. You then use this file in your main code.
As an example, suppose you have a program that needs to know the number of bits that are set in a given byte. You could do this manually whenever you need:
int count_bits(uint8_t b)
{
int count = 0;
while (b) {
count += b & 1;
b >>= 1;
}
return count;
}
Or you can generate the table in a separate program:
int main()
{
FILE *header = fopen("bitcount.h", "w");
if (!header) {
perror("fopen failed");
exit(1);
}
fprintf(header, "int bit_counts[256] = {\n");
int count;
unsigned v;
for (v=0,count=0; v<256; v++) {
uint8_t b = v;
while (b) {
count += b & 1;
b >>= 1;
}
fprintf(header, " %d,\n" count);
}
fprintf(header, "};\n");
fclose(header);
return 0;
}
This create a file called bitcount.h that looks like this:
int bit_counts[256] = {
0,
1,
1,
2,
...
7,
};
That you can include in your "real" code.

Related

How to make a list with just the used inputs for a C module

I have a large module that uses a very large input buffer, consisting of many structures which, in turn, contain other structures and in the end each structure has several variables.
Out of these hundreds of input variables, my module (standalone C entity) uses only a fraction.
I would like to know if there is a way to make a list that will contain only the variables used in my module (would be perfect if it contains the variable type and links to structure/s that contains it).
I tried Doxygen (1.8.5) but I could generate a doc with all input variables, only.
[Later EDIT]
I add an example code and the desired outcome:
#include <stdio.h>
typedef struct subS1{
unsigned char bIn1;
unsigned char bIn2;
} subS1;
typedef struct S1{
struct subS1 stMySubStruct1;
struct subS1 stMySubStruct2;
struct subS1 stMySubStruct3;
} MyInputStruct_t;
void Foo1(MyInputStruct_t *Input);
void Foo2(MyInputStruct_t *Input);
MyInputStruct_t stMyInputStruct = {{1, 2}, {0, 0}, {9, 6}}; // large input buffer
int main() {
Foo1(&stMyInputStruct); // call to my Module 'main' function
return 0;
}
void Foo1(MyInputStruct_t *Input)
{
if(Input->stMySubStruct1.bIn1 == 1)
{
printf("bIn1 = %d\n", Input->stMySubStruct1.bIn1); // stMySubStruct1.bIn1 is used (read or write)
}
Foo2(Input);
return;
}
void Foo2(MyInputStruct_t *Input)
{
if(Input->stMySubStruct3.bIn2 == 0)
{
printf("bIn2 = %d\n", Input->stMySubStruct3.bIn2); // stMySubStruct3.bIn2 is used (read or write)
}
return;
}
The list with just the used inputs for Foo1(): e.g
stMyInputStruct.stMySubStruct1.bIn1 -> is used in Foo1()
stMyInputStruct.stMySubStruct1.bIn2 -> is NOT used
...
stMyInputStruct.stMySubStruct3.bIn2 -> is used in Foo2()
This is just a five-minute hack to demonstrate what I mean, so take it with a grain of salt and for what it is.
So first I downloaded pycparser from https://github.com/eliben/pycparser/
Then I edit the C-generator from https://github.com/eliben/pycparser/blob/master/pycparser/c_generator.py
... adding two lines to the constructor-code (adding two vars struct_refs + struct_ref):
class CGenerator(object):
""" Uses the same visitor pattern as c_ast.NodeVisitor, but modified to
return a value from each visit method, using string accumulation in
generic_visit.
"""
def __init__(self, reduce_parentheses=False):
""" Constructs C-code generator
reduce_parentheses:
if True, eliminates needless parentheses on binary operators
"""
# Statements start with indentation of self.indent_level spaces, using
# the _make_indent method.
self.indent_level = 0
self.reduce_parentheses = reduce_parentheses
# newly added variables here
self.struct_refs = set()
self.struct_ref = None
Then I edit two visitor-functions, to make them save information about the struct-references they parse:
def visit_ID(self, n):
if self.struct_ref:
self.struct_refs.add(self.struct_ref + "->" + n.name)
return n.name
def visit_StructRef(self, n):
sref = self._parenthesize_unless_simple(n.name)
self.struct_ref = sref
self.struct_refs.add(sref)
res = sref + n.type + self.visit(n.field)
self.struct_ref = None
return res
Running this modified piece of Python script over your example code, collects this information:
>>> cgen.struct_refs
{'Input',
'Input->stMySubStruct1',
'Input->stMySubStruct1->bIn1',
'Input->stMySubStruct3',
'Input->stMySubStruct3->bIn2'}
So with a bit more work, it should be able to do the job more generally.
This of course breaks apart in the face of memcpy, struct-member-access-through-pointers etc.
You can also try exploiting structure in your code as well. E.g. If you always call your input-struct "Input", things gets easier.

Is it possible to dynamically load new functions to a running C program?

Consider the following C program:
#include <stdio.h>
const int OP_0 = 0;
const int OP_1 = 1;
const int OP_2 = 2;
int op_0(int x) {
return x + 2;
}
int op_1(int x) {
return x * 3 + 1;
}
int op_2(int x) {
return 2 * x * x - 10 * x + 5;
}
int compute(int op, int x) {
switch (op) {
case OP_0: return op_0(x);
case OP_1: return op_1(x);
case OP_2: return op_2(x);
}
return 0;
}
int main() {
int opcode;
int number;
printf("Enter the opcode: ");
scanf("%d", &opcode);
printf("Enter the number: ");
scanf("%d", &number);
printf("Result: %d\n", compute(opcode, number));
return 0;
}
It is a very simple program that lets the user select one of 3 operations to perform on an int input. To use this program, we can compile it with, for instance, gcc program.c -o program, and then run it with ./program. That's all obvious. Suppose, though, that we wanted to add another operation:
int op_3(int x) {
return 900 + x;
}
If we wanted to use this new operation, we'd need to recompile the entire program. Adding a new operation to this program has O(n) complexity, and is slow since it requires a complete recompilation.
My question is: is it possible, in C, to let this program add new native operations (without writing an interpreter)? In other words, is it possible to dynamically compile and add op_3 to the C program above, without having to recompile everything?
For illustration purposes, here is an example of what I have in mind:
int compute(int op, int x) {
// the first time it runs, would load `op_N.dll`
// the next time, would use the loaded version
// so, to add a new operation, we just compile
// it and add `op_N.dll` to this directory
Fun op = dynamic_load(op);
return op(x);
}
The only way I can think of is to compile a new dynamic library that is then opened by the program using dlopen()...
Another way, similar but perhaps more primitive, would be to compile the code into an object file and then load it into a mmaped region with execution permissions, jumping then to it using a function pointer.
To do this, compile the new function using gcc -c, clean the binary code from the headers with objcopy -O binary -j .text. Now in the program open() the resulting file and use the mmap() function to map this file in memory, giving as protections PROT_READ | PROT_EXEC. You'd look up the manuals for all this functions.
Note that I am assuming that you are on a unix system. I don't know much about Windows, but I imagine that something similar could be done with VirtualAlloc().
Well, what you are asking is the "Open Principle of SOLID". To do so, you need to have a dynamic dlsym obviously after dlopen. To have a dynamic dlsym you need to be able to read header files or a file with the proper function prototypes. Yes, you need to cast function pointers, but the typecast depends upon the types of your parameter list.
Edit:
Hard coding dlsym means you have to relink your import library to your executable every time you add a function to your shared object.
OR
You have two shared objects. One is the import library, and the other is the library that you want to add functionality. As David Wheeler said, "All problems of computer science could be solved with another level of indirection, except for the problem with too many layers of indirection.".
Complete noob-proof answer. As the other answers suggested, we can use dlopen and dlsym to dynamically load a shared library on C. First of all, let's create the lib. Save the following file as 0.c
int fn(int x) {
return x * 10;
}
Then, run the following command to create the shared lib:
clang -shared 0.c -o 0
Now, we must edit our compute function to load fn from 0.c dynamically and use it. First, we declare an fn : int -> int function pointer:
int (*fn)(int);
Then, we convert the operation to decimal (since we saved the shared lib as 0, no extension):
char file[256];
sprintf(file, "%d", 0);
Then, we load 0 dynamically:
void *handle = dlopen(file, RTLD_LAZY);
Then, we find fn on that lib, and assing to the fn function pointer:
*(void**)(&fn) = dlsym(LIB[op], "fn");
Then, we can just call it!
fn(5) // will return 50
Here is a complete example, that handles errors and stores the function pointers in a jump table (so we don't need to re-load the lib every time, obviously!):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
const int MAX_OPS = 256;
// Jump-table with available functions
int (*OP[MAX_OPS])(int);
// Array with shared libraries
void* LIB[MAX_OPS];
// Loads an operation dynamically
void load_op(int op) {
int (*fn)(int);
// Generates the file name
char file[256];
sprintf(file, "%d", op);
// Opens the dynamic lib
LIB[op] = dlopen(file, RTLD_LAZY);
// Handles error opening the lib
if (!LIB[op]) {
fprintf(stderr, "Couldn't load operation: %s\n", dlerror());
}
// Creates the function pointer
*(void**)(&fn) = dlsym(LIB[op], "fn");
// Handles error finding the function pointer
if (!fn) {
fprintf(stderr, "Couldn't load operation: %s\n", dlerror());
dlclose(LIB[op]);
}
// Adds to jump table
OP[op] = fn;
}
// Clears the dynlib objects
void close_ops() {
for (int op = 0; op < MAX_OPS; ++op) {
dlclose(LIB[op]);
}
}
// Applies the specified operation to an input
// Requires a shared object file with a name equivalent to the decimal
// representation of op to be loaded on the current directory
int compute(int op, int x) {
if (!OP[op]) {
load_op(op);
}
return OP[op](x);
}
int main() {
int opcode;
int number;
printf("Enter the opcode: ");
scanf("%d", &opcode);
printf("Enter the number: ");
scanf("%d", &number);
printf("Result: %d\n", compute(opcode, number));
return 0;
}
All the credit to the people who took their time to answer my question here and on #c on Libera.Chat. Thank you!

Determine the number of return values expected by Lua from inside a C extension function

Every time a function in Lua is called, the number of return values is immediately known at the call site:
f() --0
local a, b = f() --2
local t = {f()} --LUA_MULTRET
local t = {f(), nil} --1
The same is true for the C API: both lua_call and lua_pcall are always provided with the expected number of return values (or LUA_MULTRET).
Thinking about performance, it might be advantageous for a Lua function to be able to determine the number of expected return values expressed by their callers, so as to avoid computing return values that the caller did not request (if the computation takes a long time):
int getstrings(lua_State *L)
{
if(lua_numresults(L) > 0)
{
lua_pushstring(L, "a");
if(lua_numresults(L) > 1)
{
lua_pushstring(L, "b");
if(lua_numresults(L) > 2)
{
lua_pushstring(L, "c");
return 3
}
return 2;
}
return 1
}
return 0;
}
Assuming a hypothetical lua_numresults returns size_t, this function would produce only the that are really needed and will not take time to compute values that are guaranteed to be lost.
Another interesting example would be functions that return sequences:
int range(lua_State *L)
{
size_t num = lua_numresults(L);
for(size_t i = 1; i <= num; i++)
{
lua_pushinteger(L, i);
}
return num;
}
The sequence is not "lazy" so something like f(range()) cannot be done, but local a, b, c = range() returning 1, 2, 3 etc. might find its uses.
Is there anything like lua_numresults or a way to implement its functionality?
Judging from Lua 5.3 sources, the expected number of results is located in the CallInfo structure. A new call info is created for every Lua call, and the most recent one is stored in lua_State::ci There doesn't appear to be any function that can return this value, but if one has access to the structure, it is fairly straightforward to get it:
#include "lua/lstate.h"
size_t lua_numresults(lua_State *L)
{
return (size_t)L->ci->nresults;
}

How to make external Mathematica functions interruptible?

I had an earlier question about integrating Mathematica with functions written in C++.
This is a follow-up question:
If the computation takes too long I'd like to be able to abort it using Evaluation > Abort Evaluation. Which of the technologies suggested in the answers make it possible to have an interruptible C-based extension function? How can "interruptibility" be implemented on the C side?
I need to make my function interruptible in a way which will corrupt neither it, nor the Mathematica kernel (i.e. it should be possible to call the function again from Mathematica after it has been interrupted)
For MathLink - based functions, you will have to do two things (On Windows): use MLAbort to check for aborts, and call MLCallYieldFunction, to yield the processor temporarily. Both are described in the MathLink tutorial by Todd Gayley from way back, available here.
Using the bits from my previous answer, here is an example code to compute the prime numbers (in an inefficient manner, but this is what we need here for an illustration):
code =
"
#include <stdlib.h>
extern void primes(int n);
static void yield(){
MLCallYieldFunction(
MLYieldFunction(stdlink),
stdlink,
(MLYieldParameters)0 );
}
static void abort(){
MLPutFunction(stdlink,\" Abort \",0);
}
void primes(int n){
int i = 0, j=0,prime = 1, *d = (int *)malloc(n*sizeof(int)),ctr = 0;
if(!d) {
abort();
return;
}
for(i=2;!MLAbort && i<=n;i++){
j=2;
prime = 1;
while (!MLAbort && j*j <=i){
if(i % j == 0){
prime = 0;
break;
}
j++;
}
if(prime) d[ctr++] = i;
yield();
}
if(MLAbort){
abort();
goto R1;
}
MLPutFunction(stdlink,\"List\",ctr);
for(i=0; !MLAbort && i < ctr; i++ ){
MLPutInteger(stdlink,d[i]);
yield();
}
if(MLAbort) abort();
R1: free(d);
}
";
and the template:
template =
"
void primes P((int ));
:Begin:
:Function: primes
:Pattern: primes[n_Integer]
:Arguments: { n }
:ArgumentTypes: { Integer }
:ReturnType: Manual
:End:
";
Here is the code to create the program (taken from the previous answer, slightly modified):
Needs["CCompilerDriver`"];
fullCCode = makeMLinkCodeF[code];
projectDir = "C:\\Temp\\MLProject1";
If[! FileExistsQ[projectDir], CreateDirectory[projectDir]]
pname = "primes";
files = MapThread[
Export[FileNameJoin[{projectDir, pname <> #2}], #1,
"String"] &, {{fullCCode, template}, {".c", ".tm"}}];
Now, here we create it:
In[461]:= exe=CreateExecutable[files,pname];
Install[exe]
Out[462]= LinkObject["C:\Users\Archie\AppData\Roaming\Mathematica\SystemFiles\LibraryResources\
Windows-x86-64\primes.exe",161,10]
and use it:
In[464]:= primes[20]
Out[464]= {2,3,5,7,11,13,17,19}
In[465]:= primes[10000000]
Out[465]= $Aborted
In the latter case, I used Alt+"." to abort the computation. Note that this won't work correctly if you do not include a call to yield.
The general ideology is that you have to check for MLAbort and call MLCallYieldFunction for every expensive computation, such as large loops etc. Perhaps, doing that for inner loops like I did above is an overkill though. One thing you could try doing is to factor the boilerplate code away by using the C preprocessor (macros).
Without ever having tried it, it looks like the Expression Packet functionality might work in this way - if your C code goes back and asks mathematica for some more work to do periodically, then hopefully aborting execution on the mathematica side will tell the C code that there is no more work to do.
If you are using LibraryLink to link external C code to the Mathematica kernel, you can use the Library callback function AbortQ to check if an abort is in progress.

Pretty-printing a binary tree in C (and other imperative languages)

(First-time poster and rather new in programming, so be patient, please!)
I'm interested in both an efficient general algorithm for printing formatted binary trees (in a CLI environment) and a C implementation. Here is some code that I wrote myself for fun (this is a much simplified version of the original and part of a larger program supporting many BST operations, but it should compile just fine):
#include <stdbool.h> // C99, boolean type support
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define DATATYPE_IS_DOUBLE
#define NDEBUG // disable assertions
#include <assert.h>
#define WCHARBUF_LINES 20 // def: 20
#define WCHARBUF_COLMS 800 // def: 80 (using a huge number, like 500, is a good idea,
// in order to prevent a buffer overflow :)
#define RECOMMENDED_CONS_WIDTH 150
#define RECOMMENDED_CONS_WIDTHQ "150" // use the same value, quoted
/* Preprocessor directives depending on DATATYPE_IS_* : */
#if defined DATATYPE_IS_INT || defined DATATYPE_IS_LONG
#define DTYPE long int
#define DTYPE_STRING "INTEGER"
#define DTYPE_PRINTF "%*.*ld"
#undef DATATYPE_IS_CHAR
#elif defined DATATYPE_IS_FLOAT
#define DTYPE float
#define DTYPE_STRING "FLOAT"
#define DTYPE_PRINTF "%*.*f"
#undef DATATYPE_IS_CHAR
#elif defined DATATYPE_IS_DOUBLE
#define DTYPE double
#define DTYPE_STRING "DOUBLE"
#define DTYPE_PRINTF "%*.*lf"
#undef DATATYPE_IS_CHAR
#elif defined DATATYPE_IS_CHAR
#define DTYPE char
#define DTYPE_STRING "CHARACTER"
#define DTYPE_PRINTF "%*.*c" /* using the "precision" sub-specifier ( .* ) with a */
/* character will produce a harmless compiler warning */
#else
#error "DATATYPE_IS_* preprocessor directive undefined!"
#endif
typedef struct node_struct {
DTYPE data;
struct node_struct *left;
struct node_struct *right;
/* int height; // useful for AVL trees */
} node;
typedef struct {
node *root;
bool IsAVL; // useful for AVL trees
long size;
} tree;
static inline
DTYPE get_largest(node *n){
if (n == NULL)
return (DTYPE)0;
for(; n->right != NULL; n=n->right);
return n->data;
}
static
int subtreeheight(node *ST){
if (ST == NULL)
return -1;
int height_left = subtreeheight(ST->left);
int height_right = subtreeheight(ST->right);
return (height_left > height_right) ? (height_left + 1) : (height_right + 1);
}
void prettyprint_tree(tree *T){
if (T == NULL) // if T empty, abort
return;
#ifndef DATATYPE_IS_CHAR /* then DTYPE is a numeric type */
/* compute spaces, find width: */
int width, i, j;
DTYPE max = get_largest(T->root);
width = (max < 10) ? 1 :
(max < 100) ? 2 :
(max < 1000) ? 3 :
(max < 10000) ? 4 :
(max < 100000) ? 5 :
(max < 1000000) ? 6 :
(max < 10000000) ? 7 :
(max < 100000000) ? 8 :
(max < 1000000000) ? 9 : 10;
assert (max < 10000000000);
width += 2; // needed for prettier results
#if defined DATATYPE_IS_FLOAT || defined DATATYPE_IS_DOUBLE
width += 2; // because of the decimals! (1 decimal is printed by default...)
#endif // float or double
int spacesafter = width / 2;
int spacesbefore = spacesafter + 1;
//int spacesbefore = ceil(width / 2.0);
#else /* character input */
int i, j, width = 3, spacesbefore = 2, spacesafter = 1;
#endif // #ifndef DATATYPE_IS_CHAR
/* start wchar_t printing, using a 2D character array with swprintf() : */
struct columninfo{ // auxiliary structure
bool visited;
int col;
};
wchar_t wcharbuf[WCHARBUF_LINES][WCHARBUF_COLMS];
int line=0;
struct columninfo eachline[WCHARBUF_LINES];
for (i=0; i<WCHARBUF_LINES; ++i){ // initialization
for (j=0; j<WCHARBUF_COLMS; ++j)
wcharbuf[i][j] = (wchar_t)' ';
eachline[i].visited = false;
eachline[i].col = 0;
}
int height = subtreeheight(T->root);
void recur_swprintf(node *ST, int cur_line, const wchar_t *nullstr){ // nested function,
// GCC extension!
float offset = width * pow(2, height - cur_line);
++cur_line;
if (eachline[cur_line].visited == false) {
eachline[cur_line].col = (int) (offset / 2);
eachline[cur_line].visited = true;
}
else{
eachline[cur_line].col += (int) offset;
if (eachline[cur_line].col + width > WCHARBUF_COLMS)
swprintf(wcharbuf[cur_line], L" BUFFER OVERFLOW DETECTED! ");
}
if (ST == NULL){
swprintf(wcharbuf[cur_line] + eachline[cur_line].col, L"%*.*s", 0, width, nullstr);
if (cur_line <= height){
/* use spaces instead of the nullstr for all the "children" of a NULL node */
recur_swprintf(NULL, cur_line, L" ");
recur_swprintf(NULL, cur_line, L" ");
}
else
return;
}
else{
recur_swprintf(ST->left, cur_line, nullstr);
recur_swprintf(ST->right, cur_line, nullstr);
swprintf(wcharbuf[cur_line] + eachline[cur_line].col - 1, L"("DTYPE_PRINTF"",
spacesbefore, 1, ST->data);
//swprintf(wcharbuf[cur_line] + eachline[cur_line].col + spacesafter + 1, L")");
swprintf(wcharbuf[cur_line] + eachline[cur_line].col + spacesafter + 2, L")");
}
}
void call_recur(tree *tr){ // nested function, GCC extension! (wraps recur_swprintf())
recur_swprintf(tr->root, -1, L"NULL");
}
call_recur(T);
/* Omit empty columns: */
int omit_cols(void){ // nested function, GCC extension!
int col;
for (col=0; col<RECOMMENDED_CONS_WIDTH; ++col)
for (line=0; line <= height+1; ++line)
if (wcharbuf[line][col] != ' ' && wcharbuf[line][col] != '\0')
return col;
return 0;
}
/* Use fputwc to transfer the character array to the screen: */
j = omit_cols() - 2;
j = (j < 0) ? 0 : j;
for (line=0; line <= height+1; ++line){ // assumes RECOMMENDED_CONS_WIDTH console window!
fputwc('\n', stdout); // optional blanc line
for (i=j; i<j+RECOMMENDED_CONS_WIDTH && i<WCHARBUF_COLMS; ++i)
fputwc(wcharbuf[line][i], stdout);
fputwc('\n', stdout);
}
}
(also uploaded to a pastebin service, in order to preserve syntax highlighting)
It works quite well, although the automatic width setting could be better. The preprocessor magic is a bit silly (or even ugly) and not really related to the algorithm, but it allows using various data types in the tree nodes (I saw it as a chance to experiment a bit with the preprocessor - remember, I am a newbie!).
The main program is supposed to call
system("mode con:cols="RECOMMENDED_CONS_WIDTHQ" lines=2000");
before calling prettyprint_tree(), when running inside cmd.exe .
Sample output:
(106.0)
(102.0) (109.0)
(101.5) NULL (107.0) (115.0)
NULL NULL (106.1) NULL (113.0) NULL
NULL NULL NULL NULL
Ideally, the output would be like this (the reason I'm using the wprintf() family of functions is being able to print Unicode characters anyway):
(107.0)
┌─────┴─────┐
(106.1) NULL
┌───┴───┐
NULL NULL
So, my questions:
What do you think about this code? (Coding style suggestions are also very welcome!)
Can it be extended in an elegant way in order to include the line-drawing characters? (Unfortunately, I don't think so.)
Any other algorithms in C or other imperative languages (or imperative pseudo-code)?
Somewhat unrelated: What's your opinion about nested functions (non-portable GNU extension)? I think it's an elegant way to write recursive parts of a function without having to provide all the local variables as arguments (and also useful as an implementation-hiding technique), but it could be my Pascal past :-) I'm interested in the opinion of more experienced coders.
Thank you in advance for your responses!
PS. The question is not a duplicate of this one.
edit:
Jonathan Leffler wrote an excellent answer that will most probably become the "accepted answer" after a few days (unless someone posts something equally awesome!). I decided to respond here instead of commenting because of the space constraints.
The code above is actually part of a larger "homework" project (implementing BST operations in a shared library + a CLI app using that library). However, the "prettyprint" function was not part of the requirements; just something I decided to add myself.
I also added a "convert to AVL without rotations" function, that used "arraystr" as an intermediate representation ;-) I forgot that it wasn't used here. I've edited the code to remove it. Also, the bool IsAVL struct member is anything but unused; just not used in this particular function. I had to copy/paste code from various files and make a lot of changes in order to present the code cited above. That's a problem that I don't know how to solve. I would gladly post the whole program, but it is too large and commented in my mother-tongue (not in English!).
The whole project was about 1600 LOC (including comments) with multiple build targets (debug/release/static-linking) and it compiled cleanly with -Wall and -Wextra enabled. Assertions and debug messages were enabled/disabled automatically depending on the build target. Also I thought that function prototypes weren't needed for nested functions, after all nested functions do not implement any external interface by definition - GCC certainly didn't complain here. I don't know why there are so many warnings on OSX :(
I'm using GCC 4.4.1 on Windows 7.
Despite writing and testing this program on Windows, I am actually a Linux user... Still, I can't stand vim and I use nano (inside GNU screen) or gedit instead (shoot me)! In any case, I prefer the K&R brace style :)
Portability doesn't really matter, for Linux users GCC is pretty much de facto... The fact that it also works well under Windows is a nice bonus.
I'm not using a VCS, perhaps I should. I want to try, but all of them seem too complex for my needs and I don't know how to choose one :-)
You are definitely right about checking for depth overflow, thankfully it is very easy to add.
Thanks for the L' ' advice!
I find your suggestion (encapsulating "the whole of the drawing code so that the screen image and related information is in a single structure") extremely interesting... but I don't really understand what you mean as "encapsulation". Could you, please, provide 3 or 4 lines of (pseudo)code showing a possible function declaration and/or a possible function call?
This is my first "large-ish" (and non-trivial) program and I'm really thankful for your advice.
edit #2:
Here is an implementation of the "quick and dirty" method mentioned here.
(edit #3: I decided to split it to a separate answer, since it is a valid answer to the OP.)
Many responses mentioned Graphviz. I already knew about it (many Linux apps are linked against it) but I thought it would be overkill for a 10KB CLI executable. However, I'll keep it in mind for the future. It seems great.
You need to decide on whether your code needs to be portable. If you might ever need to use a compiler other than GCC, the nested functions are lethal to your portability goal. I would not use them - but my portability goals may not be the same as yours.
Your code is missing <wchar.h>; it compiles fairly cleanly without it - GCC complained about missing prototypes for your non-static functions and for swprintf() and fputwc()), but adding <wchar.h> generates a lot of serious warnings related to swprintf(); they are actually diagnosing a bug.
gcc -O -I/Users/jleffler/inc -std=c99 -Wall -Wextra -Wmissing-prototypes \
-Wstrict-prototypes -Wold-style-definition -c tree.c
tree.c:88:6: warning: no previous prototype for ‘prettyprint_tree’
tree.c: In function ‘prettyprint_tree’:
tree.c:143:10: warning: no previous prototype for ‘recur_swprintf’
tree.c: In function ‘recur_swprintf’:
tree.c:156:17: warning: passing argument 2 of ‘swprintf’ makes integer from pointer without a cast
/usr/include/wchar.h:135:5: note: expected ‘size_t’ but argument is of type ‘int *’
tree.c:156:17: error: too few arguments to function ‘swprintf’
/usr/include/wchar.h:135:5: note: declared here
tree.c:160:13: warning: passing argument 2 of ‘swprintf’ makes integer from pointer without a cast
/usr/include/wchar.h:135:5: note: expected ‘size_t’ but argument is of type ‘int *’
tree.c:174:22: warning: passing argument 2 of ‘swprintf’ makes integer from pointer without a cast
/usr/include/wchar.h:135:5: note: expected ‘size_t’ but argument is of type ‘int *’
tree.c:174:22: warning: passing argument 3 of ‘swprintf’ makes pointer from integer without a cast
/usr/include/wchar.h:135:5: note: expected ‘const wchar_t * restrict’ but argument is of type ‘int’
tree.c:177:13: warning: passing argument 2 of ‘swprintf’ makes integer from pointer without a cast
/usr/include/wchar.h:135:5: note: expected ‘size_t’ but argument is of type ‘int *’
tree.c:177:13: error: too few arguments to function ‘swprintf’
/usr/include/wchar.h:135:5: note: declared here
tree.c: In function ‘prettyprint_tree’:
tree.c:181:10: warning: no previous prototype for ‘call_recur’
tree.c:188:9: warning: no previous prototype for ‘omit_cols’
(This is GCC 4.5.2 on MacOS X 10.6.5.)
Do look up the interface to swprintf(); it is more like snprintf() than sprintf() (which is A Good Thing™!).
The overall idea is interesting. I suggest choosing one representation when submitting your code for analysis, and cleaning up anything that is not relevant to the code analysis. For example, the arraystr type is defined but unused - you don't want to let people like me get cheap shots at your code. Similarly with the unused structure members; don't even leave them as comments, even if you might want to keep them in the code in your VCS (though why?). You are using a version control system (VCS), aren't you? And that's a rhetorical question - if you aren't using a VCS, start using one now, before you lose something you value.
Design-wise, you want to avoid doing things like requiring the main program to run an obscure system() command - your code should take care of such issues (maybe with an initializer function, and perhaps a finalizer function to undo the changes made to the terminal settings).
One more reason not to like nested functions: I can't work out how to get a declaration of the function in place. What seemed like plausible alternatives did not work - but I didn't go and read the GCC manual on them.
You check for column-width overflow; you do not check for depth overflow. Your code will crash and burn if you create a tree that is too deep.
Minor nit: you can tell people who do not use 'vi' or 'vim' to edit - they don't put the opening brace of a function in column 1. In 'vi', the opening brace in column 1 gives you an easy way to the start of a function from anywhere inside it ('[[' to jump backwards; ']]' to jump to the start of the next function).
Don't disable assertions.
Do include a main program and the relevant test data - it means people can test your code, instead of just compiling it.
Use wide-character constants instead of casts:
wcharbuf[i][j] = (wchar_t)' ';
wcharbuf[i][j] = L' ';
Your code creates a big screen image (20 lines x 800 columns in the code) and fills in the data to be printed. That's a reasonable way to do it. With care, you could arrange to handle the line-drawing characters. However, I think you would need to rethink the core drawing algorithms. You would probably want to encapsulate the whole of the drawing code so that the screen image and related information is in a single structure, which can be passed by reference (pointer) to functions. You'd have a set of functions to draw various bits at positions your tree-searching code designates. You would have a function to draw the data value at an appropriate position; you would have a function to draw lines at appropriate positions. You would probably not have nested functions - it is, to my eyes, far harder to read the code when there's a function nested inside another. Making functions static is good; make the nested functions into static (non-nested) functions. Give them the context they need - hence the encapsulation of the screen image.
Overall a good start; lots of good ideas. Lots still to do.
Request for information on encapsulation...
You could use a structure such as:
typedef struct columninfo Colinfo;
typedef struct Image
{
wchar_t image[WCHARBUF_LINES][WCHARBUF_COLUMNS];
Colinfo eachline[WCHARBUF_LINES];
} Image;
Image image;
You might find it convenient and/or sensible to add some extra members; that would show up during the implementation. You might then create a function:
void format_node(Image *image, int line, int column, DTYPE value)
{
...
}
You could also make some of the constants, such as spacesafter into enum values:
enum { spacesafter = 2 };
These can then be used by any of the functions.
Coding style: The prettyprint_tree() function juggles too much computation and data to be comfortable to read. Initialization and printing of the image buffer can for example be placed in separate functions and the width computation also. I am sure you can write a formula with log to replace the
width = (max < 10) ? 1 :
(max < 100) ? 2 :
(max < 1000) ? 3 :
...
computation.
I am not used to reading nested functions and C, which makes it much harder for me to scan your code. Unless you don't share your code with others or have ideological reasons for tying the code to GCC, I wouldn't use those extensions.
Algorithm: For a quick and dirty pretty-printer, written in C, I would never use your style of layout. In comparison to your algorithm, it is a no-brainer to write an in-order traversal to print
a
/ \
b c
as
c
a
b
and I don't mind having to tilt my head. For anything prettier than that I would much rather emit
digraph g { a -> b; a -> c; }
and leave it to dot to do the formatting.
This code should work its from:http://www.ihas1337code.com/2010/09/how-to-pretty-print-binary-tree.html
#include <fstream>
#include <iostream>
#include <deque>
#include <iomanip>
#include <sstream>
#include <string>
#include <cmath>
using namespace std;
struct BinaryTree {
BinaryTree *left, *right;
int data;
BinaryTree(int val) : left(NULL), right(NULL), data(val) { }
};
// Find the maximum height of the binary tree
int maxHeight(BinaryTree *p) {
if (!p) return 0;
int leftHeight = maxHeight(p->left);
int rightHeight = maxHeight(p->right);
return (leftHeight > rightHeight) ? leftHeight + 1: rightHeight + 1;
}
// Convert an integer value to string
string intToString(int val) {
ostringstream ss;
ss << val;
return ss.str();
}
// Print the arm branches (eg, / \ ) on a line
void printBranches(int branchLen, int nodeSpaceLen, int startLen, int nodesInThisLevel, const deque<BinaryTree*>& nodesQueue, ostream& out) {
deque<BinaryTree*>::const_iterator iter = nodesQueue.begin();
for (int i = 0; i < nodesInThisLevel / 2; i++) {
out << ((i == 0) ? setw(startLen-1) : setw(nodeSpaceLen-2)) << "" << ((*iter++) ? "/" : " ");
out << setw(2*branchLen+2) << "" << ((*iter++) ? "\\" : " ");
}
out << endl;
}
// Print the branches and node (eg, ___10___ )
void printNodes(int branchLen, int nodeSpaceLen, int startLen, int nodesInThisLevel, const deque<BinaryTree*>& nodesQueue, ostream& out) {
deque<BinaryTree*>::const_iterator iter = nodesQueue.begin();
for (int i = 0; i < nodesInThisLevel; i++, iter++) {
out << ((i == 0) ? setw(startLen) : setw(nodeSpaceLen)) << "" << ((*iter && (*iter)->left) ? setfill('_') : setfill(' '));
out << setw(branchLen+2) << ((*iter) ? intToString((*iter)->data) : "");
out << ((*iter && (*iter)->right) ? setfill('_') : setfill(' ')) << setw(branchLen) << "" << setfill(' ');
}
out << endl;
}
// Print the leaves only (just for the bottom row)
void printLeaves(int indentSpace, int level, int nodesInThisLevel, const deque<BinaryTree*>& nodesQueue, ostream& out) {
deque<BinaryTree*>::const_iterator iter = nodesQueue.begin();
for (int i = 0; i < nodesInThisLevel; i++, iter++) {
out << ((i == 0) ? setw(indentSpace+2) : setw(2*level+2)) << ((*iter) ? intToString((*iter)->data) : "");
}
out << endl;
}
// Pretty formatting of a binary tree to the output stream
// # param
// level Control how wide you want the tree to sparse (eg, level 1 has the minimum space between nodes, while level 2 has a larger space between nodes)
// indentSpace Change this to add some indent space to the left (eg, indentSpace of 0 means the lowest level of the left node will stick to the left margin)
void printPretty(BinaryTree *root, int level, int indentSpace, ostream& out) {
int h = maxHeight(root);
int nodesInThisLevel = 1;
int branchLen = 2*((int)pow(2.0,h)-1) - (3-level)*(int)pow(2.0,h-1); // eq of the length of branch for each node of each level
int nodeSpaceLen = 2 + (level+1)*(int)pow(2.0,h); // distance between left neighbor node's right arm and right neighbor node's left arm
int startLen = branchLen + (3-level) + indentSpace; // starting space to the first node to print of each level (for the left most node of each level only)
deque<BinaryTree*> nodesQueue;
nodesQueue.push_back(root);
for (int r = 1; r < h; r++) {
printBranches(branchLen, nodeSpaceLen, startLen, nodesInThisLevel, nodesQueue, out);
branchLen = branchLen/2 - 1;
nodeSpaceLen = nodeSpaceLen/2 + 1;
startLen = branchLen + (3-level) + indentSpace;
printNodes(branchLen, nodeSpaceLen, startLen, nodesInThisLevel, nodesQueue, out);
for (int i = 0; i < nodesInThisLevel; i++) {
BinaryTree *currNode = nodesQueue.front();
nodesQueue.pop_front();
if (currNode) {
nodesQueue.push_back(currNode->left);
nodesQueue.push_back(currNode->right);
} else {
nodesQueue.push_back(NULL);
nodesQueue.push_back(NULL);
}
}
nodesInThisLevel *= 2;
}
printBranches(branchLen, nodeSpaceLen, startLen, nodesInThisLevel, nodesQueue, out);
printLeaves(indentSpace, level, nodesInThisLevel, nodesQueue, out);
}
int main() {
BinaryTree *root = new BinaryTree(30);
root->left = new BinaryTree(20);
root->right = new BinaryTree(40);
root->left->left = new BinaryTree(10);
root->left->right = new BinaryTree(25);
root->right->left = new BinaryTree(35);
root->right->right = new BinaryTree(50);
root->left->left->left = new BinaryTree(5);
root->left->left->right = new BinaryTree(15);
root->left->right->right = new BinaryTree(28);
root->right->right->left = new BinaryTree(41);
cout << "Tree pretty print with level=1 and indentSpace=0\n\n";
// Output to console
printPretty(root, 1, 0, cout);
cout << "\n\nTree pretty print with level=5 and indentSpace=3,\noutput to file \"tree_pretty.txt\".\n\n";
// Create a file and output to that file
ofstream fout("tree_pretty.txt");
// Now print a tree that's more spread out to the file
printPretty(root, 5, 0, fout);
return 0;
}
Maybe you can take a look at the Bresenham's line algorithm that it could be suitable for you
Here is a C implementation of the "quick and dirty" method mentioned here. It doesn't get much quicker and/or dirtier:
void shittyprint_tree(tree *T){ // Supposed to be quick'n'dirty!
// When DTYPE is "char", width is a bit larger than needed.
if (T == NULL)
return;
const int width = ceil(log10(get_largest(T->root)+0.01)) + 2;
const wchar_t* sp64 = L" ";
void nested(node *ST, int spaces){ // GCC extension
if (ST == NULL){
wprintf(L"\n"); // Can be commented to disable the extra blanc line.
return;
}
nested(ST->right, spaces + width);
wprintf(L"%*.*s("DTYPE_PRINTF")\n", 0, spaces, sp64, 1, 1, ST->data);
nested(ST->left, spaces + width);
}
nested(T->root, 2);
}
Sample output (using the same tree as before):
(115.0)
(113.0)
(109.0)
(107.0)
(106.1)
(106.0)
(102.0)
(101.5)
I can't say, though, that it fits my original requirements...

Resources