Writing your own file I/O plugin


Introduction

Writing a file I/O plugin is as simple as subclassing the agfio_plugin class, which declares three virtual methods: one load() and two overloaded store()'s. So in general, the steps for writing a file I/O plugin are as follows:

  1. subclass agfio_plugin
  2. override the virtual methods with your own load() and store()'s
There is a little final touch to be done, and it's explained in the following example. For the details of the agfio_plugin class, please see the agfio_plugin section of the AGLIB API documentation.


Example

In this example, we create a toy plugin called MyFormat.

MyFormat.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#ifndef _MYFORMAT_H_
#define _MYFORMAT_H_

#include <ag/agfio_plugin.h>

class MyFormat: public agfio_plugin
{
public:
  virtual list<AGId>
  load(const string& filename,
       const Id& id = "",
       map<string,string>* signalInfo = NULL,
       map<string,string>* options = NULL)
    throw (agfio::LoadError);

  virtual string
  store(const string& filename,
        const Id& id = "",
        map<string,string>* options = NULL)
    throw (agfio::StoreError);
};

AGFIO_PLUGIN(MyFormat);

#endif

Line 4
The agfio_plugin class is defined in
ag/agfio_plugin.h, so you have to include that.

Line 6
The plugin name (=class name) is MyFormat, and it's a subclass of agfio_plugin.

Lines 9-20
The load() and one of store()'s are overridden. If not overridden, the default behavior of these methods is to throw an exception saying that the method is not supported. About the exceptions thrown: load() throws agfio::LoadError store()'s throw agfio::StoreError.

Another important thing to keep in mind: When your load/store routines encounter an error that can't be handled, make them throw agfio::LoadError (for load()) or agfio::StoreError (for store()'s) with some useful error message rather than exiting the program. These exceptions are propagated through the AGLIB core to the user applications. The applications can do something for those exceptions, if they decide to handle them.

Line 23
AGFIO_PLUGIN() is a macro that sets up some internal functions that are used to load/unload the plugin. The argument is the name of the plugin class. This line can appear in other files than header files, but it should be evaluted exactly once in the entire source files. For example, suppose that MyFormat-load.cc implemented the load(), and MyFormat-store.cc implemented the store(). Both of them included MyFormat.h. This is an error, because the AGFIO_PLUGIN(MyFormat) is evaluated twice: once in MyFormat-load.cc and once in MyFormat-store.cc. The solution in this case is to remove that line from the header and put it in one of the .cc files.

MyFormat.cc
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include "MyFormat.h"

list<AGId>
MyFormat::load(const string& filename,
               const Id& id,
               map<string,string>* signalInfo,
               map<string,string>* options)
  throw (agfio::LoadError)
{
  cout << "MyFormat::load" << endl;
  cout << "filename=" << filename << endl;
  list<AGId> a;
  return a;
}

string
MyFormat::store(const string& filename,
                const Id& id,
                map<string,string>* options)
  throw (agfio::StoreError)
{
  cout << "MyFormat::store" << endl;
  cout << "filename=" << filename << endl;
  cout << "id=" << id << endl;
  return "";
}

Lines 3-14
Implement load() method. It just prints a string and returns an empty list.

Lines 16-26
Implement store() method. It just prints a string and returns an empty string.


Plug it in!

Once you finish your plugin class, the next step is compilation. Here is an example:

Gcc was used for the compiler in this example. '-shared' tells g++ to make a shared object. Other compilers may have a different command line option for this. The point is that you have to make it a shared object.

If there is no error in the source code, g++ will generate agfio_plugin_MyFormat.so as a result. This is the final result of your toy file I/O plugin. Different platforms have different extensions for shared objects. For instance, Windows systems use .dll, so in those systems, you have to name your plugin like this: agfio_plugin_MyFormat.dll. The prefix, agfio_plugin_ is significant. You always have to prefix your plugin (the shared object) with it. Otherwise AGLIB won't be able to find your plugin.

To see if the plugin is working, you can compile and run the following test program. When you run the program, make sure that the directory where your plugin is located is in the system's search path (check LD_LIBRARY_PATH for UNIX-like systems or PATH for Windows systems). If the plugin directory is not in the system's search path, AGLIB can't load it.

test.cc
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <iostream>
#include <ag/AGAPI.h>

using namespace std;

int main()
{
  cout << "----------" << endl;
  cout << "loading..." << endl;
  Load("MyFormat", "inputfile.txt");

  cout << "----------" << endl;
  cout << "storing..." << endl;
  Store("MyFormat", "outputfile.txt", "Test:123");
}


Annotation Graph Toolkit