Doubly Linked List Library

Jeffrey M. Laughlin


Table of Contents

The Code
Revision Control by Mercurial
Build Scripts
SWIG Definitions
C Unit Tests
Python Example
Perl Example

In the interest of having some recent code samples to show off, I've developed this trivial doubly linked list library over the last few days. My goal is to demonstrate not only my ability to program in C, but also my ability to manage a complete project, including documentation and integration with other languages.

The core library is written in C. I've embedded documentation in the header file, and used Doxygen to extract and format it. The Doxygen output is attached to the end of this document. I've used SCons (an advanced replacement for make) to build everything. I've used SWIG to compile the library as modules for both Perl and Python, and written a trivial demonstration program for each one. I've written this document in DocBook.

The Code

Here is the C header file. You'll note the embedded Doxygen documentation:

/**
 * \mainpage Doubly Linked List Library
 * A completely useless and trivial implementation of a doubly linked list
 * library in C. Please do not use for anything serious.
 * \author Jeff Laughlin
 * \date (c)2008
*/

/**
 * \file dll.h
 * \brief Definitions for doubly linked list library
 */

/// A node in a doubly linked list
/**
 * All node fields must be initialized by the user. All operations not
 * specifically embodied in a function are the responsibility of the user.
*/
struct Node {
    struct Node *nextNode; ///<Pointer to the next Node in the list
    struct Node *prevNode; ///<Pointer to the previous Node in the list
    char *payload; ///<Pointer to a string
};

/// Given any node in a list, return the first node in the list
struct Node * getFirstNode(struct Node *node);

/// Given any node in a list, return the last node in the list
struct Node * getLastNode(struct Node *node);

/// Insert newNode in the list before rootNode
void insertBefore(struct Node *rootNode, struct Node *newNode);

/// Insert newNode in the list after rootNode
void insertAfter(struct Node *rootNode, struct Node *newNode);

/// Delete the node from the list
/**
 * Removes the given node from the list. This is done by linking it's previous
 * and next nodes to each other, and setting the given nodes previous and next
 * fields to NULL. Note that the memory used by the Node is not freed.
*/
void deleteNode(struct Node *node);

/// Search the list for a node containing searchString
/**
 * Given any node in a list, search the list for a node containing the given
 * search string and return a pointer to that node. Searches forwards, then
 * backwards from given node.
*/
struct Node * searchList(struct Node *node, char *searchString);

/// Join the strings in a list on a given string
/**
 * Given any node in a list, and a string, return the strings of all the nodes
 * in the list in list order with joinString inserted inbetween each one.
 * EG if the list contains ['foo', 'bar'], and it is joined with 'ack', the
 * result will be 'fooackbar'.
*/
char * joinList(struct Node *node, char *joinString);

Here's the actual source code:

// dll.c
// Part of the doubly linked list library
// (c) 2008 Jeff Laughlin

#include <stdlib.h>
#include <error.h>
#include <err.h>
#include <string.h>
#include <dll.h>
#define CHUNK_SIZE 1024*4

struct Node * getFirstNode(struct Node *node) {
    while (node->prevNode != NULL)
        node = node->prevNode;
    return node;
}

struct Node * getLastNode(struct Node *node) {
    while (node->nextNode != NULL)
        node = node->nextNode;
    return node;
}

void insertBefore(struct Node *rootNode, struct Node *newNode) {
    if (rootNode->prevNode != NULL) {
        rootNode->prevNode->nextNode = newNode;
        newNode->prevNode = rootNode->prevNode;
    }
    else {
        newNode->prevNode = NULL;
    }

    rootNode->prevNode = newNode;
    newNode->nextNode = rootNode;
}

void insertAfter(struct Node *rootNode, struct Node *newNode) {
    if (rootNode->nextNode != NULL) {
        rootNode->nextNode->prevNode = newNode;
        newNode->nextNode = rootNode->nextNode;
    }
    else {
        newNode->nextNode = NULL;
    }

    rootNode->nextNode = newNode;
    newNode->prevNode = rootNode;
}

void deleteNode(struct Node *node) {
    if (node->prevNode != NULL)
        node->prevNode->nextNode = node->nextNode;

    if (node->nextNode != NULL)
        node->nextNode->prevNode = node->prevNode;

    node->prevNode = NULL;
    node->nextNode = NULL;
}

struct Node * searchList(struct Node *node, char *searchString) {
    struct Node * currentNode;
    struct Node sentinalStruct;
    struct Node * sentinal = &sentinalStruct;

    // search forward
    sentinal->prevNode = NULL;
    sentinal->nextNode = node;
    currentNode = sentinal;
    do {
        currentNode = currentNode->nextNode;
        if (strcmp(currentNode->payload, searchString) == 0)
            return currentNode;
    }
    while (currentNode->nextNode != NULL);

    // search backwards
    sentinal->nextNode = NULL;
    sentinal->prevNode = node;
    currentNode = sentinal;
    do {
        currentNode = currentNode->prevNode;
        if (strcmp(currentNode->payload, searchString) == 0)
            return currentNode;
    }
    while (currentNode->prevNode != NULL);

    return NULL;
}


char * joinList(struct Node *node, char *joinString) {
    char * retString;
    size_t retStringSize = CHUNK_SIZE;
    size_t payloadLength;
    size_t retStringLength;
    size_t joinStringLength;
    struct Node dummyFirst;
    struct Node * ptrDummyFirst = &dummyFirst;

    joinStringLength = strlen(joinString);

    // move to first node
    node = getFirstNode(node);
    if (node->nextNode == NULL)
        return node->payload;

    retString = malloc(retStringSize);
    if (retString == NULL)
        err(1, "Malloc failed");
    retString[0] = (char)NULL;

    ptrDummyFirst->nextNode = node;
    node = ptrDummyFirst;

    // Iterate over nodes
    do {
        node = node->nextNode;
        // is retString big enough? realloc if necessary
        payloadLength = strlen(node->payload);
        retStringLength = strlen(retString);

        while (payloadLength + retStringLength + joinStringLength + 1 > retStringSize) {
            // Hypothetically, if the string being joined was many times larger
            // than CHUNK_SIZE, it would be faster to just allocate that many
            // chunks at once. But that would be one long string!
            if (payloadLength + joinStringLength + 1 > CHUNK_SIZE)
                retStringSize = retStringSize + (payloadLength + joinStringLength + 1) / CHUNK_SIZE * CHUNK_SIZE;
            else
                retStringSize += CHUNK_SIZE;
            retString = realloc(retString, retStringSize);
            if (retString == NULL)
                err(1, "Malloc failed");
        }

        strcat(retString, node->payload);
        if (node->nextNode != NULL)
            strcat(retString, joinString);

    }
    while (node->nextNode != NULL);

    return retString;
}

Revision Control by Mercurial

I used Mercurial as a revision control mechanism for this project. I've found Mercurial to be highly polished and easy to use, and the distributed functionality is great. Here is my worklog for this project:

laughlin@nibbler:~/tmp/dll$ hg log -v
changeset:   12:04063e6b80bc
tag:         tip
user:        Jeffrey Laughlin <jlaughlin@ucsd.edu>
date:        Thu Mar 27 16:17:28 2008 -0700
files:       doc/dll_examples/dll_examples.html doc/dll_examples/dll_examples.xml
description:
Updated report with mention of Mercurial


changeset:   11:557498ddb295
user:        Jeffrey Laughlin <jlaughlin@ucsd.edu>
date:        Thu Mar 27 15:35:17 2008 -0700
files:       dll.dox
description:
added doxygen config file


changeset:   10:9348ce525033
user:        Jeffrey Laughlin <jlaughlin@ucsd.edu>
date:        Thu Mar 27 15:34:14 2008 -0700
files:       SConscript.perl SConscript.python SConstruct dll.c dll.h dll.i \
             dll_unit.c doc/dll_examples/Makefile doc/dll_examples/dll_examples.html \
             doc/dll_examples/dll_examples.xml doc/dll_examples/style.css \
             perl/dll_example.pl python/dll_example.py
description:
Finished up examples, added docbook file for examples, added doxygen doc strings


changeset:   9:b68dba32d60a
user:        Jeffrey Laughlin <jlaughlin@ucsd.edu>
date:        Thu Mar 27 13:03:43 2008 -0700
files:       perl/dll_example.pl
description:
fixed perl example


changeset:   8:096394db27ec
user:        Jeffrey Laughlin <jlaughlin@ucsd.edu>
date:        Thu Mar 27 11:59:20 2008 -0700
files:       dll_example.py perl/dll_example.pl python/dll_example.py
description:
created perl example 


changeset:   7:cadcdddd8207
user:        Jeffrey Laughlin <jlaughlin@ucsd.edu>
date:        Thu Mar 27 11:30:45 2008 -0700
files:       SConscript.perl SConscript.python SConstruct
description:
Added perl target to scons. Put perl and python modules in seperate dirs, but its buggy


changeset:   6:79b1d26ef679
user:        Jeffrey Laughlin <jlaughlin@ucsd.edu>
date:        Thu Mar 27 10:22:50 2008 -0700
files:       dll_example.py
description:
created simple python demo program


changeset:   5:6b86d8f58ffd
user:        Jeffrey Laughlin <jlaughlin@ucsd.edu>
date:        Thu Mar 27 10:13:32 2008 -0700
files:       SConstruct dll_unit.c
description:
Finished unit test suite


changeset:   4:1067a33310dc
user:        Jeffrey Laughlin <jlaughlin@ucsd.edu>
date:        Thu Mar 27 09:44:53 2008 -0700
files:       dll_unit.c
description:
Factored list initialization out from suite init


changeset:   3:a655b29150fc
user:        Jeffrey Laughlin <jlaughlin@ucsd.edu>
date:        Thu Mar 27 09:42:42 2008 -0700
files:       SConstruct dll.c dll_unit.c
description:
Building unit tests


changeset:   2:5fe63dfbff97
user:        Jeffrey Laughlin <jlaughlin@ucsd.edu>
date:        Wed Mar 26 21:40:41 2008 -0700
files:       dll.c dll.h dll.i
description:
Added deleteNode function


changeset:   1:eae1127089b2
user:        Jeffrey Laughlin <jlaughlin@ucsd.edu>
date:        Wed Mar 26 21:34:49 2008 -0700
files:       SConstruct dll.c dll.h dll.i
description:
Added search list function, fixed swig interface


changeset:   0:efda71edd302
user:        Jeffrey Laughlin <jlaughlin@ucsd.edu>
date:        Wed Mar 26 21:19:07 2008 -0700
files:       SConstruct dll.c dll.h dll.i
description:
Built basic doubly linked list library, SWIG interface, and SCONS file

Build Scripts

I used the SCons build system for this project. It is a highly portable and flexable make replacement.

Here is the main script:

# SConstruct
# Part of the doubly linked list library
# (c)2008 Jeffrey Laughlin

import distutils.sysconfig
import os

pythonEnv = Environment(SWIGFLAGS=['-python'],
                  CPPPATH=['.', distutils.sysconfig.get_python_inc()],
                  SHLIBPREFIX="")
Export('pythonEnv')
SConscript('SConscript.python', build_dir='python')

perlPath = os.popen("perl -e 'use Config; print $Config{sitearch};'").read()
perl = Environment(SWIGFLAGS=['-perl'],
                  CPPPATH=['.', '/usr/lib/perl/5.8.8/CORE', perlPath],
                  CFLAGS=['-D_GNU_SOURCE'],
                  SHLIBPREFIX="")
Export('perl')
SConscript('SConscript.perl', build_dir='perl')

env = Environment(
    CPPPATH=['.'],
)
sl = env.SharedLibrary('doublylinkedlist', 'dll.c')

unitTest = Environment(
    CPPPATH=['.'],
    CFLAGS=['-g']
)
dll_unit = unitTest.Program('dll_unit', ['dll.c', 'dll_unit.c'], LIBS='cunit')

Default(sl)

Also there is a supporting script to build DLL as a Perl module:

# SConscript.perl
# Part of the doubly linked list library
# (c)2008 Jeff Laughlin
# Builds the perl module. It works.

Import('perl')
perlModFile = File("doublylinkedlist.so")
perlModule = perl.SharedLibrary(perlModFile, ("dll.c", "dll.i"))
#perlModule = SharedLibrary(perlModFile, ("dll.c", "dll.i"))

And a supporting script to build the Python module:

# SConscript.python
# Part of the doubly linked list library
# (c)2008 Jeff Laughlin
# Builds the python module. It's slightly broken. Interestingly, the perl one
# is almost exactly the same and it works fine.

Import('pythonEnv')
pymodFile = File("_doublylinkedlist.so")
#pythonModule = SharedLibrary(pymodFile, ("dll.c", "dll.i"))
pythonModule = pythonEnv.SharedLibrary(pymodFile, ("dll.c", "dll.i"))

SWIG Definitions

I used SWIG to build both Perl and Python modules from a common C codebase. This is the SWIG interface file I created for this project:

/* dll.i
 * SWIG Interface file
 * Part of the doubly linked list library
 * (c)2008 Jeff Laughlin
*/

%module doublylinkedlist
%{
#include <dll.h>
%}

struct Node {
    struct Node *nextNode;
    struct Node *prevNode;
    char *payload;
};

struct Node * getFirstNode(struct Node *node);

struct Node * getLastNode(struct Node *node);

void insertBefore(struct Node *rootNode, struct Node *newNode);

void insertAfter(struct Node *rootNode, struct Node *newNode);

void deleteNode(struct Node *node);

struct Node * searchList(struct Node *node, char *searchString);

char * joinList(struct Node *node, char *joinString);

C Unit Tests

This full suite of unit tests tries to cover all of the corner cases.

// dll_unit.c
// (c)2008 Jeffrey Laughlin
// Part of the doubly linked list library
// CUnit based unit tests. Just compile and run.

#include <stdlib.h>
#include <dll.h>
#include <CUnit/Basic.h>

struct Node *threeNodes[3];
struct Node *twoNodes[2];
struct Node *oneNode;

int init_suite1(void) {
    threeNodes[0] = malloc(sizeof (struct Node));
    threeNodes[1] = malloc(sizeof (struct Node));
    threeNodes[2] = malloc(sizeof (struct Node));
    twoNodes[0] = malloc(sizeof (struct Node));
    twoNodes[1] = malloc(sizeof (struct Node));
    oneNode = malloc(sizeof (struct Node));
    return 0;
}


void setupNodes(void) {
    threeNodes[0]->prevNode = NULL;
    threeNodes[0]->nextNode = threeNodes[1];
    threeNodes[0]->payload = "Cherokee";
    threeNodes[1]->prevNode = threeNodes[0];
    threeNodes[1]->nextNode = threeNodes[2];
    threeNodes[1]->payload = "Comanchee";
    threeNodes[2]->prevNode = threeNodes[1];
    threeNodes[2]->nextNode = NULL;
    threeNodes[2]->payload = "Chief";

    twoNodes[0]->prevNode = NULL;
    twoNodes[0]->nextNode = twoNodes[1];
    twoNodes[0]->payload = "Wrangler";
    twoNodes[1]->prevNode = twoNodes[0];
    twoNodes[1]->nextNode = NULL;
    twoNodes[1]->payload = "CJ";

    oneNode->prevNode = NULL;
    oneNode->nextNode = NULL;
    oneNode->payload = "Jeepster";
}


int clean_suite1(void) {
    free(threeNodes[0]);
    free(threeNodes[1]);
    free(threeNodes[2]);
    free(twoNodes[0]);
    free(twoNodes[1]);
    free(oneNode);
    return 0;
}


void test_getFirstNode(void) {
    setupNodes();

    CU_ASSERT_PTR_EQUAL(getFirstNode(threeNodes[0]), threeNodes[0]);
    CU_ASSERT_PTR_EQUAL(getFirstNode(threeNodes[1]), threeNodes[0]);
    CU_ASSERT_PTR_EQUAL(getFirstNode(threeNodes[2]), threeNodes[0]);

    CU_ASSERT_PTR_EQUAL(getFirstNode(twoNodes[0]), twoNodes[0]);
    CU_ASSERT_PTR_EQUAL(getFirstNode(twoNodes[1]), twoNodes[0]);

    CU_ASSERT_PTR_EQUAL(getFirstNode(oneNode), oneNode);
}


void test_getLastNode(void) {
    setupNodes();

    CU_ASSERT_PTR_EQUAL(getLastNode(threeNodes[0]), threeNodes[2]);
    CU_ASSERT_PTR_EQUAL(getLastNode(threeNodes[1]), threeNodes[2]);
    CU_ASSERT_PTR_EQUAL(getLastNode(threeNodes[2]), threeNodes[2]);

    CU_ASSERT_PTR_EQUAL(getLastNode(twoNodes[0]), twoNodes[1]);
    CU_ASSERT_PTR_EQUAL(getLastNode(twoNodes[1]), twoNodes[1]);

    CU_ASSERT_PTR_EQUAL(getLastNode(oneNode), oneNode);
}


void test_insertBefore(void) {
    setupNodes();

    insertBefore(twoNodes[0], oneNode);
    CU_ASSERT_PTR_EQUAL(twoNodes[0]->prevNode, oneNode);
    CU_ASSERT_PTR_EQUAL(twoNodes[0]->nextNode, twoNodes[1]);
    CU_ASSERT_PTR_EQUAL(oneNode->nextNode, twoNodes[0]);
    CU_ASSERT_PTR_EQUAL(oneNode->prevNode, NULL);

    twoNodes[0]->prevNode = NULL;
    insertBefore(twoNodes[1], oneNode);
    CU_ASSERT_PTR_EQUAL(twoNodes[1]->prevNode, oneNode);
    CU_ASSERT_PTR_EQUAL(twoNodes[1]->nextNode, NULL);
    CU_ASSERT_PTR_EQUAL(oneNode->nextNode, twoNodes[1]);
    CU_ASSERT_PTR_EQUAL(oneNode->prevNode, twoNodes[0]);
    CU_ASSERT_PTR_EQUAL(twoNodes[0]->nextNode, oneNode);
    CU_ASSERT_PTR_EQUAL(twoNodes[0]->prevNode, NULL);
}


void test_insertAfter(void) {
    setupNodes();
    
    insertAfter(twoNodes[1], oneNode);
    CU_ASSERT_PTR_EQUAL(twoNodes[1]->nextNode, oneNode);
    CU_ASSERT_PTR_EQUAL(twoNodes[1]->prevNode, twoNodes[0]);
    CU_ASSERT_PTR_EQUAL(oneNode->prevNode, twoNodes[1]);
    CU_ASSERT_PTR_EQUAL(oneNode->nextNode, NULL);

    twoNodes[1]->nextNode = NULL;
    insertAfter(twoNodes[0], oneNode);
    CU_ASSERT_PTR_EQUAL(twoNodes[1]->prevNode, oneNode);
    CU_ASSERT_PTR_EQUAL(twoNodes[1]->nextNode, NULL);
    CU_ASSERT_PTR_EQUAL(oneNode->nextNode, twoNodes[1]);
    CU_ASSERT_PTR_EQUAL(oneNode->prevNode, twoNodes[0]);
    CU_ASSERT_PTR_EQUAL(twoNodes[0]->nextNode, oneNode);
    CU_ASSERT_PTR_EQUAL(twoNodes[0]->prevNode, NULL);
}


void test_deleteNode(void) {
    setupNodes();

    deleteNode(threeNodes[1]);
    CU_ASSERT_PTR_EQUAL(threeNodes[1]->prevNode, NULL);
    CU_ASSERT_PTR_EQUAL(threeNodes[1]->nextNode, NULL);
    CU_ASSERT_PTR_EQUAL(threeNodes[0]->nextNode, threeNodes[2]);
    CU_ASSERT_PTR_EQUAL(threeNodes[2]->prevNode, threeNodes[0]);

    deleteNode(threeNodes[0]);
    CU_ASSERT_PTR_EQUAL(threeNodes[0]->prevNode, NULL);
    CU_ASSERT_PTR_EQUAL(threeNodes[0]->nextNode, NULL);
    CU_ASSERT_PTR_EQUAL(threeNodes[2]->nextNode, NULL);
    CU_ASSERT_PTR_EQUAL(threeNodes[2]->prevNode, NULL);

    deleteNode(threeNodes[0]);
    CU_ASSERT_PTR_EQUAL(threeNodes[0]->prevNode, NULL);
    CU_ASSERT_PTR_EQUAL(threeNodes[0]->nextNode, NULL);
    CU_PASS("Didn't freak out on deleteing a one node list");
}


void test_searchList(void) {
    setupNodes();

    CU_ASSERT_PTR_EQUAL(searchList(threeNodes[0], "Chief"), threeNodes[2]);
    CU_ASSERT_PTR_EQUAL(searchList(threeNodes[2], "Chief"), threeNodes[2]);
    CU_ASSERT_PTR_EQUAL(searchList(threeNodes[2], "Cherokee"), threeNodes[0]);
    CU_ASSERT_PTR_EQUAL(searchList(threeNodes[0], "Cherokee"), threeNodes[0]);
    
    CU_ASSERT_PTR_EQUAL(searchList(oneNode, "Jeepster"), oneNode);

    CU_ASSERT_PTR_EQUAL(searchList(threeNodes[2], "foobar"), NULL);
}


void test_joinList(void) {
    setupNodes();

    CU_ASSERT_STRING_EQUAL(joinList(threeNodes[0], " "), 
            "Cherokee Comanchee Chief");

    CU_ASSERT_STRING_EQUAL(joinList(twoNodes[0], " "), 
            "Wrangler CJ");

    CU_ASSERT_STRING_EQUAL(joinList(threeNodes[2], " and "), 
            "Cherokee and Comanchee and Chief");

    CU_ASSERT_STRING_EQUAL(joinList(oneNode, " "), 
            "Jeepster");
}


int main() {
    CU_pSuite pSuite = NULL;

    /* initialize the CUnit test registry */
    if (CUE_SUCCESS != CU_initialize_registry())
        return CU_get_error();

    /* add a suite to the registry */
    pSuite = CU_add_suite("Suite_1", init_suite1, clean_suite1);
    if (NULL == pSuite) {
       CU_cleanup_registry();
       return CU_get_error();
    }

    /* add the tests to the suite */
    if ((NULL == CU_add_test(pSuite, "test of getFirstNode", test_getFirstNode))
            || (NULL == CU_add_test(pSuite, "test of getLastNode", test_getLastNode))
            || (NULL == CU_add_test(pSuite, "test of insertBefore", test_insertBefore))
            || (NULL == CU_add_test(pSuite, "test of insertAfter", test_insertAfter))
            || (NULL == CU_add_test(pSuite, "test of searchList", test_searchList))
            || (NULL == CU_add_test(pSuite, "test of joinList", test_joinList))
            || (NULL == CU_add_test(pSuite, "test of deleteNode", test_deleteNode)))
    {
       CU_cleanup_registry();
       return CU_get_error();
    }

    /* Run all tests using the CUnit Basic interface */
    CU_basic_set_mode(CU_BRM_VERBOSE);
    CU_basic_run_tests();
    CU_cleanup_registry();
    return CU_get_error();
}

Here is the output:


     CUnit - A Unit testing framework for C - Version 2.1-0
     http://cunit.sourceforge.net/


Suite: Suite_1
  Test: test of getFirstNode ... passed
  Test: test of getLastNode ... passed
  Test: test of insertBefore ... passed
  Test: test of insertAfter ... passed
  Test: test of searchList ... passed
  Test: test of joinList ... passed
  Test: test of deleteNode ... passed

--Run Summary: Type      Total     Ran  Passed  Failed
               suites        1       1     n/a       0
               tests         7       7       7       0
               asserts      53      53      53       0

Python Example

Here is a trivial program to demonstrate the use of the DLL built as a Python module:

#!/usr/bin/python
# dll_example.py
# Part of the doubly linked list library
# (c)2008 Jeff Laughlin
# Demonstrates basic functionality of the DLL built as a python module with
# SWIG.

from doublylinkedlist import *

node1 = Node()
node2 = Node()
node3 = Node()
node1.prevNode = None
node1.nextNode = node2
node1.payload = "Jeep"
node2.prevNode = node1
node2.nextNode = node3
node2.payload = "Ford"
node3.prevNode = node2
node3.nextNode = None
node3.payload = "Chevy"

print ("Node 1: " + repr(node1))
print ("Node 2: " + repr(node2))
print ("Node 3: " + repr(node3))

print ("Node containing 'Ford': " + repr(searchList(node1, "Ford")))

print ("List joined on ' and ': " + joinList(node1, " and "))

Here is the output produced:

laughlin@nibbler:~/tmp/dll$ python/dll_example.py
Node 1: <doublylinkedlist.Node; proxy of <Swig Object of type 'struct Node *' at 0x81c0048> >
Node 2: <doublylinkedlist.Node; proxy of <Swig Object of type 'struct Node *' at 0x81c3750> >
Node 3: <doublylinkedlist.Node; proxy of <Swig Object of type 'struct Node *' at 0x81c37a0> >
Node containing 'Ford': <doublylinkedlist.Node; proxy of <Swig Object of type 'struct Node *' at 0x81c3750> >
List joined on ' and ': Jeep and Ford and Chevy

Perl Example

Here is a trivial program to demonstrate the use of the DLL built as a Perl module:

#!/usr/bin/perl
# dll_example.pl
# Part of the doubly linked list library
# (c)2008 Jeff Laughlin
# Demonstrates basic functionality of the DLL built as a perl module with SWIG.

use strict;
use doublylinkedlist;

my $node1 = doublylinkedlist::Node->new();
my $node2 = doublylinkedlist::Node->new();
my $node3 = doublylinkedlist::Node->new();

$node1->{prevNode} = undef;
$node1->{nextNode} = $node2;
$node2->{prevNode} = $node1;
$node2->{nextNode} = $node3;
$node3->{prevNode} = $node2;
$node3->{nextNode} = undef;

$node1->{payload} = "Rice";
$node2->{payload} = "Wheat";
$node3->{payload} = "Corn";

printf( "%s\n", doublylinkedlist::joinList($node1, " is not "));

Here is the output produced:

laughlin@nibbler:~/tmp/dll/perl$ ./dll_example.pl
Rice is not Wheat is not Corn