Go Down

Topic: moving function map into library (Read 650 times) previous topic - next topic

KRavEN

I'm trying to write library for interfacing with some hardware via serial.  I've got things working well like this in a sketch:

Code: [Select]
const static struct {
  const char *name;
  void (*func)(char *);
} function_map [] = {
  { "status", processStatusLine },
};

void processStatusLine(char * buffer) {
  Serial.println((char*)buffer);
}

int call_function(const char *name, char * var) {
  int i;
  for (i = 0; i < (sizeof(function_map) / sizeof(function_map[0])); i++) {
    if (!strcmp(function_map[i].name, name) && function_map[i].func) {
      function_map[i].func(var);
      return 0;
    }
  }
  return -1;
}

void setup () {
  Serial.begin(9600);
  char * buffer;
  call_function("status", buffer);
}

void loop() {
}

How do I move the function_map and call_function into my HardwareClass.h and HardwareClass.cpp?  I've tried quite a few things and couldn't get it working.

pYro_65

#1
Feb 25, 2013, 06:21 am Last Edit: Feb 25, 2013, 06:25 am by pYro_65 Reason: 1
This kind of layout below should work, ( untested ) and function_map is not available outside of the Cpp, if it needs to be usable in the sketch file, you'll need a variable marked extern.

Edit: or you will have to make processStatusLine visible to the cpp, by placing its declaration in the header ( can leave the definition in the sketch )

Header:
Code: [Select]

struct function_map_type{
 const char *name;
 void (*func)(char *);
};

int call_function(const char *name, char * var);


Cpp
Code: [Select]
#include "Arduino.h"

static const function_map_type function_map[] = { { "status", processStatusLine }, };

int call_function(const char *name, char * var) {
 int i;
 for (i = 0; i < (sizeof(function_map) / sizeof(function_map[0])); i++) {
   if (!strcmp(function_map[i].name, name) && function_map[i].func) {
     function_map[i].func(var);
     return 0;
   }
 }
 return -1;
}

KRavEN

That's not working either.  I also think I need the struct to be private to the object:

Here's what I was trying that's not working

header
Code: [Select]
#ifndef HwClassSerial_h
#define HwClassSerial_h

#include "Arduino.h"

class HwClassSerial
{
  public:
    HwClassSerial(HardwareSerial &_hwClassSerial);
   
  private:
    HardwareSerial* hwClassSerial;   
    void init();
   
    struct _function_map{
      char *name;
      void (*func)(char *);
    };
   
    int _call_function(const char *name, char * var);
    void _processStatusLine(char * buffer);


Also tried this under private
Code: [Select]
    typedef struct {
      char *name;
      void (HwClassSerial::*func)(char *);
    } function_map[];
   
    static function_map _function_map;


cpp
Code: [Select]
#include "Arduino.h"
#include "HwClassSerial.h"

static const _function_map function_map[] = { { "status", _processStatusLine }, };

HwClassSerial::HwClassSerial(HardwareSerial &_hwClassSerial) {
  hwClassSerial = &_hwClassSerial;
  init(); 
}

void HwClassSerial::init()

  hwClassSerial->begin(9600);
  static function_map _function_map  = {
    { "status", _processStatusLine },
  };
}

int HwClassSerial::_call_function(const char * name, char * var) {
  int i;

  for (i = 0; i < (sizeof(_function_map) / sizeof(_function_map[0])); i++) {
    if (!strcmp(_function_map[i].name, name) && _function_map[i].func) {
      //_function_map[i].func(var);
      return 0;
    }
  }

  return -1;
}

void HwClassSerial::_processStatusLine(char * buffer) {
}

pYro_65

Code: [Select]
    typedef struct {
      char *name;
      void (HwClassSerial::*func)(char *);
    } function_map[];
   
    static function_map _function_map;


static function_map _function_map; is invalid, function_map is declared as an array of 'anonymous structs'. Don't worry about typedefs on structs, they are not necessary for this usage in C++.

What I had does work, here is a working example you can modify.

Sketch file:
Code: [Select]

#include "a.h"

void processStatusLine(char * buffer) {
  Serial.println((char*)buffer);
}

void setup()
  {
    Serial.begin( 9600 );
    call_function( "status", "test string" );
    return;
  }

void loop(){ return; }


Header ( a.h )
Code: [Select]
struct function_map_type{
  const char *name;
  void (*func)(char *);
};

int call_function(const char *name, char * var);
void processStatusLine(char * buffer);


Source ( a.cpp )
Code: [Select]
#include "a.h"
#include "Arduino.h"

static const function_map_type function_map[] = { { "status", processStatusLine }, };

int call_function(const char *name, char * var) {
  int i;
  for (i = 0; i < (sizeof(function_map) / sizeof(function_map[0])); i++) {
    if (!strcmp(function_map[i].name, name) && function_map[i].func) {
      function_map[i].func(var);
      return 0;
    }
  }
  return -1;
}

KRavEN

Yes, that does work that way. 

Is it possible for the processStatusLine, function_map_struct, and call_function to all be within the scope of a HwClassSerial class instance instead of in the global scope?  The reason for this is I have several pieces of gear all controlled by serial so I'm trying to setup a base class for each to extend.  Each child class would define it's own function_map_type and there could be several child classes instantiated simultaneously.

pYro_65

Quote
Is it possible for the processStatusLine, function_map_struct, and call_function to all be within the scope of a HwClassSerial class instance instead of in the global scope?


Yes, using the correct working code I provided, you should be able to put them in. However not for the way you want.
To keep with ease of use, processStatusLine and other functions to be called via a pointer need to be non-member functions( static or friends ).

But really, you are mixing two different systems when one will do. call_function is a lookup method, whereas using an inherited/base type should imply that the derived type provides the missing information, which completes the base type.

KRavEN

The problem I'm trying to solve is that I will have several device classes.  For code re-usability I want to have a base class that has a few private methods.
Code: [Select]

// sends a command to a serial device and sends each received byte from the output to _processIncomingByte
void _processCommand(const char * commandName, const int longestLine, int linesToRead);

// this takes each received byte and constructs character arrays to be processed.  The method to process the
// character array is determined by the mapping of the commandName
void _processIncomingByte(const byte c, char * buffer, const char * commandName, const int lineLength);


So each class that extends the base class will have a different set of commands to run and methods to process those commands.  Once class could have:
Code: [Select]
struct {
  const char *name;
  void (*func)(char *);
} function_map [] = {
  { "status", processStatusLine },
  { "update", processUpdateLine },
};


and another class could have:
Code: [Select]
struct {
  const char *name;
  void (*func)(char *);
} function_map [] = {
  { "config", processConfigLine },
  { "reset", processRestLine },
};


Each process function should also be private to each class.  I hope that makes sense. :)

pYro_65

#7
Feb 25, 2013, 11:56 pm Last Edit: Feb 25, 2013, 11:57 pm by pYro_65 Reason: 1
If you want the idea encapsulated in a class, and only one serial device is communicating at once, then things are easy using classes.

Imagine your base class is something like this.
Code: [Select]
struct BaseType{
 /*
   Pure virtual as you will not be creating instances of this class, only instances of
   classes that derive this base class.
 */
 virtual void RunCommand( const char *c_Data ) = 0;
};


Code: [Select]
struct DerivedA : BaseType{

 void RunCommand( const char *c_Data ){

   if( strcmp( "status", c_Data ) ){

   }else if( strcmp( "update", c_Data ) ){

   }
 }
};

struct DerivedB : BaseType{

 void RunCommand( const char *c_Data ){

   if( strcmp( "config", c_Data ) ){

   }else if( strcmp( "reset", c_Data ) ){

   }    
 }
};



Code: [Select]
DerivedA objA;
DerivedB objB;

BaseType *currentObj = &objA;

void loop(){

char c_Buffer[ x ];
//...  fill c_buffer with command.

//Run command based on current object.
currentObj->RunCommand( c_Buffer );
}

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy