Development workflow

Discussions on development, Auryn's inner workings, adding new features and how to change things at the core
Post Reply
gillett
Posts: 5
Joined: Fri Feb 03, 2017 10:23 pm

Development workflow

Post by gillett »

Hey!

I'm having a bit of trouble compiling Auryn with a new connection class that I've written. I've created a new header and source file in /src/auryn, and I've added the header file path to auryn.h. I then run cmake ../../../ again from within the build tree (/build/linux/release), and make, and this creates a new static library. When I reference the new class in a simulation however the compiler complains that it wasn't declared in the current scope.

What am I forgetting to do? I should note that changes to existing classes work after I rebuild the library. Is this the best workflow for making modifications?
User avatar
zenke
Site Admin
Posts: 156
Joined: Tue Oct 14, 2014 11:34 am
Location: Basel, CH
Contact:

Re: Development workflow

Post by zenke »

Hi Gillet,

when the compiler complains this is typically a sign for it being unable to find the header file. It is a bit difficult to pinpoint the problem directly without any additional information. One thing that often happens for instance when you copy an existing connection object, is that one forgets to update the include guards. However, without more information and logs its impossible to tell where exactly the problem is.

I would, however, recommend to not add your new classes directly to the library, but compile them directly into your program. During development this approach avoids having to rebuild the Auryn library at each step. The way to do that is to put YourConnection.h and YourConnection.cpp with the simulation code you are writing and then adding a Makefile with the following flavor:

Code: Select all

# shorthand for your MPI C++ compiler
CC = mpicxx
# Your Auryn install path
AURYNDIR = /home/username/auryn/
# Path to libauryn.a
AURYNLIBDIR = $(AURYNDIR)/build/release/src/

CFLAGS= -ansi -Wall -pipe -O3 -DNDEBUG -ffast-math -funsafe-math-optimizations \
        -march=native -mtune=native -pedantic \
        -I/usr/local/include -I$(AURYNDIR)/src -I$(AURYNDIR)/dev/src

LDFLAGS=$(AURYNLIBDIR)/libauryn.a \
        -L/usr/local/lib -lboost_program_options -lboost_serialization -lboost_mpi -lboost_system -lboost_filesystem

LOCALOBJECTS=YourConnection.o
SIMULATIONS=sim_yoursim

.SECONDARY:

all: $(SIMULATIONS)

sim_%: sim_%.o $(LOCALOBJECTS)
    $(CC) $(CFLAGS) $(LOCALOBJECTS) $< $(LDFLAGS) -o $(subst .o,,$<)

%.o : %.cpp
    $(CC) $(CFLAGS) -c $<

clean: 
    rm -f *.o $(SIMULATIONS)
This example assumes you named your simulation "sim_yoursim". Now, in "sim_yoursim.cpp" you have to include the Auryn library and your connection class that you are developing

Code: Select all

#include "auryn.h"
#include "YourConnection.h"
Only once your code has stabilized and you do not want to change it all the time any, I would recommend moving it to the library itself. In that latter case this will then save you the last include in the example above. In doing so hope also debugging the problem will be a bit easier.

I hope that helps.

Cheers,

Friedemann
gillett
Posts: 5
Joined: Fri Feb 03, 2017 10:23 pm

Re: Development workflow

Post by gillett »

I figured it out -- I had copied one of the STDP connection classes to use as a starting point and had forgotten to rename the include guards. Rebuilding with cmake works now.

I had tried before to modify my Makefile exactly as you described, but I was getting the same error. It turns out that the include order matters, and that YourConnection.h needs to be declared before auryn.h. Maybe some compilers won't let you add to the scope of a static library? Anyways, thanks for the help.

- Max
User avatar
zenke
Site Admin
Posts: 156
Joined: Tue Oct 14, 2014 11:34 am
Location: Basel, CH
Contact:

Re: Development workflow

Post by zenke »

Great that you were able to solve your problem.
I am not sure about the inclusion order of the header files. However, the order of the object files as passed to the linker does typically matter. Anyway, glad it works now.
User avatar
zenke
Site Admin
Posts: 156
Joined: Tue Oct 14, 2014 11:34 am
Location: Basel, CH
Contact:

Re: Development workflow

Post by zenke »

Hi Max,
today I found a bug in SpikeDelay, which I believe should be fixed by this commit https://github.com/fzenke/auryn/commit/ ... d015835eea
This is just to give you a heads up in case you used this function to implement your spike delay.
Cheers,
F
gillett
Posts: 5
Joined: Fri Feb 03, 2017 10:23 pm

Re: Development workflow

Post by gillett »

Thanks! This does affect me.

Unrelated, but do you have advice for how to write a program where connection types are determined at run time? I've been using preprocessor conditional groups to compile separate programs, each with different combinations of plasticity, but lately this has become unwieldy. I know that C++ isn't a very dynamic language, but there should be a way to initialize a Connection, and dynamically recast this to (for example) an STDPConnection, right? I suppose that declaring a bunch of virtual methods in Connection could solve this issue, but that doesn't seem very maintainable.
User avatar
zenke
Site Admin
Posts: 156
Joined: Tue Oct 14, 2014 11:34 am
Location: Basel, CH
Contact:

Re: Development workflow

Post by zenke »

Hi Max,
this is always going to be a bit ugly, but what you can do is something like this:

Code: Select all

SparseConnection * con;

if ( condition ) {
 con = new SparseConnection(foo, bar);
} else {
 STDPFancyConnection * tmp = new STDPFancyConnection(foo, bar, foo2, bar2, ... );
 tmp->set_something(x);
 tmp->do_somehing_else(y);
 con = tmp;
}
if you wrap the whole thing into a SparseConnection * get_connection( condition )
function, the code remains half way readable.

Not sure if this gets close to answering your question.
F
gillett
Posts: 5
Joined: Fri Feb 03, 2017 10:23 pm

Re: Development workflow

Post by gillett »

This approach gets me most of the way there, but I still need to conditionally access methods and modify attributes that aren't declared in SparseConnection (such as stdp_active) later in the code. I also need to conditionally pass some of the connection objects to constructors that accept a derived type. This doesn't work, as the variables are still of SparseConnection type.

I can declare virtual methods in SparseConnection, and modify the constructors to accept any SparseConnection, but this doesn't seem very good practice. Do you have any suggestions?
User avatar
zenke
Site Admin
Posts: 156
Joined: Tue Oct 14, 2014 11:34 am
Location: Basel, CH
Contact:

Re: Development workflow

Post by zenke »

My only suggestion is to type cast the sparse connection objects when you need to access member functions/variables.

Something along the lines of

Code: Select all

SparseConnection * con = new ComplicatedConnection( ... );

if ( complex_connection_type ) {
 ((ComplicatedConnection *) con)->foo_bar();
}
I guess you can use a template function to simplify the typecasting. It's not pretty, but surely better than introducing virtual functions in SparseConnection which are not defined in most descendants.
Post Reply