0

I'm currently working on my own arduino library and I'm becoming exasperated with the following problem: I want to store data with the extended database library (https://code.google.com/p/arduino-edb/) on a sd card. I tried to follow and adapt the instructions on this page and I gave my custom library a property "File dbFile" as well as a property "EDB db". Additionally I have to pass two function pointers to the constructor of the EDB library. So my custom library got two more properties: a writer and a reader function. My class looks roughly like that:

class myLib
{
   public:
      myLib();
      ~myLib();
      void configWriter( unsigned long address, byte data );
      byte configReader( unsigned long address );
      File dbFile;
      EDB configDB;
}

...so far so good. But here comes the pain in the ... When I'm trying to instantiate a new EDB object in the constructor of my class by passing two pointers to the member functions I'm constantly getting an error. Here is how I call the constructor of the EDB library:

configDB = new EDB( &myLib::configWriter, &myLib::configReader );

I get the following error:

no matching function for call to 'EDB::EDB()'

[...] note: candidates are:

note: EDB::EDB(void ()(long unsigned int, uint8_t), uint8_t ()(long unsigned int)) EDB(EDB_Write_Handler *, EDB_Read_Handler *);

The prototype of the EDB constructor looks like this:

EDB(EDB_Write_Handler *, EDB_Read_Handler *);

EDB_Write_Handler and EDB_Read_Handler are defined like so:

typedef void EDB_Write_Handler(unsigned long, const uint8_t); typedef uint8_t EDB_Read_Handler(unsigned long);

So I assume that my constructor call doesn't work because the functions that I'm passing are from a foreign class: (myLib::) and not just ()

I have no idea how to solve that problem. Does someone of you have any idea?

Thanks, Andy

EDIT: Here is a Minimal, Complete, and Verifiable example:

myLib.h:

#ifndef myLib_h
#define myLib_h

#include "Arduino.h"
#include <SPI.h>
#include <SD.h>
#include <EDB.h>

#define SD_CS 4
#define SD_SS 10

class myLib
{
  public:
    myLib();
    ~myLib();
    void begin();
    void configWriter( unsigned long address, byte data );
    byte configReader( unsigned long address );
    File dbFile;
    EDB * configDB;

};

#endif

myLib.cpp:

#include "myLib.h"

myLib::myLib( void )
{}

myLib::~myLib()
{}

void myLib::begin()
{
  // Init SD  
  pinMode(SS, OUTPUT);
  SD.begin(SD_CS);

  // Load Config
  configDB = new EDB( &myLib::configWriter, &myLib::configReader );
  dbFile = SD.open("config.db", FILE_WRITE);
}

void myLib::configWriter( unsigned long address, byte data )
{
  dbFile.seek(address);
  dbFile.write(data);
  dbFile.flush();
}

byte myLib::configReader( unsigned long address )
{
  dbFile.seek(address);
  return dbFile.read();
}

Arduino sketch:

#include <SPI.h>
#include <SD.h>
#include <EDB.h>
#include "myLib.h"

myLib* a;

void setup()
{
  a = new myLib();
  a->begin();
}

void loop()
{
}
  • Have you tried passing it methods of an instance instead of the class? – Ignacio Vazquez-Abrams Aug 24 '15 at 8:40
  • new EDB( &this->configWriter, &this->configReader ); like so? already tried it. same problem... – Andreas Müller Aug 24 '15 at 8:45
2

The problem here is a common one with C++. It basically boils down to how C++ handles member functions.

You define a member function as, say,

class myLib {
    void myFunc(int val);
};

When you call the function it's actually not defined as that - it's instead had its definition change to:

void myFunc(myLib *this, int val);

The addition of the myLib *this is an implicit parameter, and it's how C++ keeps track of its object instances when calling different functions.

So a function which expects a parameter of void (*func)(int) won't accept your method because it's actually void (*func)(myLib *, int). Hence the error you are seeing.

There is no easy, clean, way around it that I have found as yet, I am afraid. The closest you can do is to move the reader and writer functions outside of your class and store a global pointer to the actual class, something like:

static myLib *globalLib;
void globalConfigWriter( unsigned long address, byte data ) {
    globalLib->congigWriter(address, data);
}
byte globalConfigReader( unsigned long address ) {
    return globalLib->configReader(address);
}

Then in the constructor for your lib you can do:

myLib::myLib() {
    globalLib = this;
}

You then register the global reader and writer functions with the EDB constructor instead of the class member functions.

It does have the rather major down-side that there can only be one instance of your class since there is only one global pointer and only one pair of reader / writer functions. There are ways of supporting a finite number of instances, but it basically involves having a reader/writer pair for each instance and allocating one to an instance when it's constructed and de-allocating it when its destructed. Keeping track of them and knowing which object is associated with which function pair is tricky at best...

  • Thanks, Majenko for your easy explanation! Now I can see where the problem is. Being able to create only one instance of the class isn't a problem in my case. I'll give your solution a try and let you know how it went. – Andreas Müller Aug 24 '15 at 11:10
0

Can you post a Minimal, Complete, and Verifiable example? We don't care what the functions do, just make up an example that demonstrates the message.


Having said that, this looks wrong:

class myLib
{
   public:
      myLib();
      ~myLib();
      void configWriter( unsigned long address, byte data );
      byte configReader( unsigned long address );
      File dbFile;
      EDB configDB;
}

Here is how I call the constructor of the EDB library:

 configDB = new EDB( &myLib::configWriter, &myLib::configReader );

OK so myLib is a class, not an instance of a class.

The prototype of the EDB constructor looks like this:

    EDB(EDB_Write_Handler *, EDB_Read_Handler *);

So you want an instance of EDB_Write_Handler.


Let's say that EDB needed a dog and a cat. You don't pass "dog" and "cat" you pass "Fido" and "Fluffy". They are instances of the classes.


You have this:

configDB = new EDB( &myLib::configWriter, &myLib::configReader );

The constructor for EDB expects two functions:

EDB(EDB_Write_Handler *, EDB_Read_Handler *);

These are declared like this:

typedef void EDB_Write_Handler(unsigned long, const uint8_t);
typedef uint8_t EDB_Read_Handler(unsigned long);

These are static functions, that is, they cannot be class functions. Why not? Because a class function has an implicit pointer this which points to this instance of the class.

So, you cannot pass a non-static class-function to the EDB constructor.

Now you can make the functions configWriter and configReader static, but then they can't access class variables (eg. dbFile) because they belong to an instance of the function.

I've tried fiddling around with your code, but without a clear understanding of your intentions, I can't get it to compile. Do you mean to derive your class from EDB?

Why do you even want a class myLib? Are you planning to have multiple instances of it? If so, why? If not, why make a class?

  • Thanks for your reply Nick! I'm quite familiar with the difference between a class and an instance (aka object) of a class but I'm a little unsure when it comes to the syntax of c++. Thanks for your suggestion! I've added a minimal, complete, and verifiable example. – Andreas Müller Aug 24 '15 at 9:41
  • See amended answer. – Nick Gammon Aug 24 '15 at 10:20
  • This is not at all an Arduino problem. It is a C++ problem. – Nick Gammon Aug 24 '15 at 10:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.