Friday, January 26, 2007

vector can not hold reference to a pure virtual base class

I had three classes, a template pure virtual base class, a template
derived class and a third which I would like to use to store copies of
the derived class. The code looked like this:

#include <iostream>
#include <vector>

using namespace std;

template <class _prec> class Base {
public:
_prec i;

Base() {
i = 12;
}

virtual void f() = 0;

};

template <class _prec> class Derived : public Base<_prec> {
public:
void f() {
std::cout << this->i << std::endl;
}
};

template <class _prec> class Collect {
public:
vector <Base<_prec> *> vec;

Collect() {
}

void g(Base<_prec> &in) {
vec.push_back(&in);
vec[0]->f();
}
};

int main() {
Derived<int> *d = new Derived<int>;
Collect<int> c;

c.g(*d);

return 0;
}



And resulted in errors such as:

/usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/vector.tcc:256: error: cannot allocate an object of abstract type ‘Base<int>’
base_vector_test.cpp:6: note: since type ‘Base<int>’ has pure virtual functions
/usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/vector.tcc:256: error: cannot declare variable ‘__x_copy’ to be of abstract type ‘Base<int>’
base_vector_test.cpp:6: note: since type ‘Base<int>’ has pure virtual functions



The problem is that vector creates a copy of the object you pass it. If you pass it a reference to an abstract base class it can't create a copy, just as you can't do: Base b; The solution in my case is to use pointers. This however means you'll have to manage their allocation and deallocation of the objects yourself. The fixed code looks like this:

#include <iostream>
#include <vector>

using namespace std;

template <class _prec> class Base {
public:
_prec i;

Base() {
i = 12;
}

virtual void f() = 0;

};

template <class _prec> class Derived : public Base<_prec> {
public:
void f() {
std::cout << this->i << std::endl;
}
};

template <class _prec> class Collect {
public:
vector <Base<_prec> *> vec;

Collect() {
}

void g(Base<_prec> &in) {
vec.push_back(&in);
vec[0]->f();
}
};

int main() {
Derived<int> *d = new Derived<int>;
Collect<int> c;

c.g(*d);

delete d;
return 0;
}



My original thread on comp.lang.c++ where I got help with this problem can be found here: http://groups.google.ie/group/comp.lang.c++/browse_thread/thread/c4cad258a9a94540

Thursday, January 25, 2007

Inheritance appears to break when deriving a template class

If you have a base template class from which you derive another template class inheritance can appear to break, i.e. you don't appear to be able to access the base classes variables and methods directly. For example take the following code:


#include <iostream>

template <class _prec> class Base {
public:
_prec i;

Base() {
i = 12;
}

virtual void f() {
std::cout << i << std::endl;
}

};

template <class _prec> class Derived : public Base<_prec> {
public:
void f() {
std::cout << i*2 << std::endl;
}
};

int main() {
Base<int> b;
Derived<int> d;

b.f();
d.f();

return 0;
}



On gcc 4.1.2 this gives the following error:

derive_test.cpp: In member function ‘void Derived<_prec>::f()’:
derive_test.cpp:20: error: ‘i’ was not declared in this scope



The problem is caused because the base class is not in scope, why I find unclear, and the work around a little messy. But basically if you change
std::cout << i*2 << std::endl;
to
std::cout << this->i*2 << std::endl;

All will be well.


More info at: http://www.parashift.com/c++-faq-lite/templates.html#faq-35.19 and
http://gcc.gnu.org/onlinedocs/gcc/Name-lookup.html

Friday, January 19, 2007

You don't exist, go away!

This post has moved: HERE

Thursday, January 18, 2007

Information about your session

Tips for finding out about your permissions and rights in a session, either on your own computer or a server.

whoami: Displays the username that you are currently logged in as.

who: Displays a list of users currently logged in to the computer. Useful for getting the ip address of the computer you have logged into from.

ifconfig: Displays the ip address of your computer

groups: Displays group membership for the current user.

echo $ENV_NAME: Display the setting for the environment setting ENV_NAME. For example to check which cvs server/root you are currently using type:

echo $CVSROOT

This will display the cvsroot information you are currently using. (CVS commands to follow in a new post)

uname -r

Displays the current kernel version.

More to come here.

Default editor

To change the default command line editor from say vim to nano/pico enter the following in the .bashrc file in your home directory.

export EDITOR=/usr/bin/pico

or you can do this by issuing a setenv command during a session.

eg:

setenv EDITOR=/usr/bin/pico

Backing up your home directory to a remote server using cron

install cron:

sudo apt-get install cron

Allow all users to use cron:

sudo touch /etc/cron.deny

Add a job to rsync your home directory:

crontab -e

nano should pop up, add the following line, replacing username with your username and server with the server you are backing up to:

0 1 * * * rsync -r ~ username@server:~

Exit (Ctrl-X) and say yes to save when prompted.

Because the backup script will need to login to the remote server you need to setup ssh to allow you to login using your ssh key. If you don't have an ssh key create one with the following command, accepting defaults when prompted (do not set a passphrase):

ssh-keygen

Now copy your public key to the server to allow you to login automatically:

scp ~/.ssh/id_rsa.pub username@server:~/.ssh/authorized_keys

Now test the backup by running, replacing username with your username on the server:

rsync -r ~ username@server:~

and see if your home directory is copied to the server correctly. Note with default settings rsync will not delete files that you have removed locally from the backup.


In order to solve that you can write your own script to handle the backup of the home dir. Here is an example:

#! /bin/bash
USER="username"
HOME="/home/$USER"
TARGET="/path/to/backup"

echo "Backing up files from $HOME"
echo "Backing up files to $TARGET"
rsync -Cavz --delete --delete-excluded --exclude-from=$HOME/.exclude_backup.txt $HOME $TARGET

echo "Completed"

exit 1;




This is the exclude file:
demo
tmp
.beagle
.kde
.gnome
.mozilla

Making a PDF file out of a bunch of JPEGs

1. Convert JPEG files to PS, use jpeg2ps (apt-getable)

1a. If you have a lot of files you can convert them all like this:

find . -iname "*.jpg" -print0 | xargs --replace -0 jpeg2ps {} -o {}.ps

2. Convert all the files of pdfs

find . -iname "*.ps" -print0 | xargs --replace -0 ps2pdf {} {}.pdf

3. Join pdf files with pdfjoin (arg-getable in pdfjam package)

If the files are in the correct order use:

pdfjoin *.pdf

otherwise list the files in order:

pdfjoin file1.pdf file2.pdf...