Problem linking 2 library

Hi.

I make a library “Grafcet” that have a dependency library “Step”.
Fisrt, the 2 classes was on the same header and cpp file and was working, but I want to split it on 2 file to get cleaner.

But Grafcet class do not include Step definition… Did I have something else to do that #include Step header into grafcet.

Buy now, arduino sketch only declare a grafcet object.

#include <Grafcet.h>

Grafcet grafcet;
Grafcet.h:29: undefined reference to `Step_t::Step_t(unsigned char)'

Library in attachment.

Thank,

Grafcet.cpp (1.9 KB)

Grafcet.h (1 KB)

Step.cpp (1019 Bytes)

Step.h (1004 Bytes)

Are you programming in C or C++?

typedef class Step_t{
public:
  Step_t(uint8_t _stepType = 0);
  void setAction(void (*_action)());
  void setTransition(transition_t (*_transition)());
  void setNextStep(Step_t *_nextStep);
  virtual void execute(); //execute the action
  bool validate() const;
  bool transitionIsSet() const;
  Step_t *getNextStep() const;
  bool getFlag(uint8_t _flag) const;
  void setFlag(uint8_t x, bool logic);
  uint8_t getStepType() const;
private:
  Step_t *nextStep = NULL;
  transitionPtr transition = NULL;
  void (*action)() = NULL; //do the action in the state //TODO if null, standy by state 
  flag_t flag;
protected:
  uint8_t stepType;
}*StepPtr;

humm as newbie as I am, I'm kind not able to make the difference.. it is c++ on arduino so i guest...

nitrof:
humm as newbie as I am, I'm kind not able to make the difference.. it is c++ on arduino so i guest...

typedef class Step_t{

in C++ you don't need to typedef structs or enums (and certainly not classes)... they are automatically typedef'ed. your syntax is a (legacy) C requirement, so you can jettison all of that

why:

}*StepPtr;

if you define a class:

class myClass{

};

FYI, you create a pointer to a class like this:

myClass* someInstance;

I cannot understand exacty what it is you are trying to do... do you want each instance of Grafcet to contain a single or multiple Step_t instance(s)?

A single instance manage a linked list of step. as far as the grafcet "evoluate" it point to the next step.

It is a state transition manager.I try to do a library to create step and transition like you illustrate a grafcet.

kind like SFC.

But a newbie programmer version... :stuck_out_tongue:

myClass* someInstance;

I know i can do that. but I find it friendly to use typedef pointer when you have to use it a lot.

I tried to remove typedef from class definition, it did not fix the compiling issue.

I found what was wrong:

In step constructor I have a optionnal parameter:

Step_t(uint8_t _stepType = 0);

It seem that creating it in another header cause some error fo be left emty:

const StepPtr initialPtr = new Step_t();  ///not compile

const StepPtr initialPtr = new Step_t(0); //compile

It is strange that it have work when it was on the same file...

Thank for the help !

[EDIT] hum... seem not right yet. I have mute all function into the sketch, juste declaring Grafcet object... now when I try to use function, I can't acces it:

request for member 'initial' in 'grafcet', which is of non-class type 'Grafcet()'

   grafcet.initial()->setTransition(&readChar_f);//first condition to star

I call it the exact same way as it was working when it was on only one library...

juste declaring Grafcet object

How?

What, exactly, does initial() return?

in the sketch declaring Grafcet object compile:

Grafcet grafcet();

but when I try to use any function of grafcet object on the sketch, it don't work:

grafcet.initial()->setTransition(&readChar_f);

initial is define:

StepPtr Grafcet::initial() const {
	return initialPtr;
}

Like I said, all this code was working when Grafcet and Step object was on the same header and cpp files...

nitrof:
Like I said, all this code was working when Grafcet and Step object was on the same header and cpp files...

post your code

grafcet header:

#ifndef Grafcet_h
#define Grafcet_h

#include <Arduino.h>
#include <step/Step.h>
//#include <pulse/Pulse.h>

//step type
#define NORMAL   0 //do action once then wait for transition
#define CONTINUOUS 1 //do action continuously while waiting for the transition
#define PULSE   3 //do action repetively at a certain interval time
#define DUTYCYCLE  4 //pulse on off a trigguer by dutycycle parameter (frequency, dutycycle)
#define TON   5 //do action after a certain time, then wait for transition
#define TOFF   6 //do action, wait a certain time, then wait for transition
#define RUNNED     7 // flag use when step have been executed
#define EXTRAFLAG  8 // for further or other use



class Grafcet{
public:
 Grafcet();
 ~Grafcet();
 //void begin();
 void handler();
 StepPtr initial() const;
 void setEmergencyStop(void (*_emergencyStop)());

private:
 const StepPtr initialPtr = new Step_t(0);
 StepPtr activeStep;
 void (*emergencyStop)() = NULL;
};
typedef Grafcet *grafcetPtr;

#endif

.ccp:

#include <Grafcet.h>

#include <Arduino.h>
#include <step/Step.h>
//#include <pulse/Pulse.h>


Grafcet::Grafcet() : activeStep(initialPtr) {
}

Grafcet::~Grafcet(){
 delete initialPtr;
}

void Grafcet::handler() {
 switch (activeStep->getStepType()) {
 case NORMAL:
 
 if(!activeStep->getFlag(RUNNED)){
 activeStep->execute();
 activeStep->setFlag(RUNNED, true);
 }
 break;
 case CONTINUOUS:
 activeStep->execute();
 break;
/* case PULSE:
 break;
 case DUTYCYCLE:
 break;
 case TON:
 break;
 case TOFF:
 break;*/
 }


//////////////Those two protection are for developpement and will freze the MCU//////////////////////////////
 /*    The programer have the responsability to make all step and transition to point to a valid object */
///////////////////////////////////////////////////////////////////////////////////////////////////////////// 
 if(!activeStep->transitionIsSet()){ //transiotion set, trig alarm
 //if(!activeStep->transition){
 Serial.println(F("No transition have been set to this step"));
 //  emergency sequence, call it deenergizer or something like it
 if(emergencyStop) emergencyStop();
 delay(3000);
 while(1); //lock MCU
 }
 if(!activeStep->getNextStep()) {
 Serial.println(F("NO NEXT STEP!!! SYSTEM LOCK! CHECK YOUR GRAFCET SEQUENCE."));
 // emergency sequence, call it deenergizer or something like it 
 if(emergencyStop) emergencyStop();
 delay(3000);
 while(1);  //lock MCU
 }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////


 if(activeStep->validate()) {
 activeStep = activeStep->getNextStep(); //if validated, point to next step 
 activeStep->setFlag(RUNNED, false);
 }
 
}

StepPtr Grafcet::initial() const {
 return initialPtr;
}

void Grafcet::setEmergencyStop(void (*_emergencyStop)()){
 emergencyStop = _emergencyStop;
}

step header:

#ifndef Step_h
#define Step_h

#include <Arduino.h>



typedef struct flag_t{
 int check(uint8_t x) const {return flag>>x & 1;};
 void set(uint8_t x, bool logic) { if(x<8){flag &= ~(1<<x); flag |= logic<<x;}};
private:
 uint8_t flag = 0;
}*flagPtr;


typedef bool transition_t;
typedef transition_t (*transitionPtr)();


class Step_t{
public:
 Step_t(uint8_t _stepType = 0);
 void setAction(void (*_action)());
 void setTransition(transition_t (*_transition)());
 void setNextStep(Step_t *_nextStep);
 virtual void execute(); //execute the action
 bool validate() const;
 bool transitionIsSet() const;
 Step_t *getNextStep() const;
 bool getFlag(uint8_t _flag) const;
 void setFlag(uint8_t x, bool logic);
 uint8_t getStepType() const;
private:
 Step_t *nextStep = NULL;
 transitionPtr transition = NULL;
 void (*action)() = NULL; //do the action in the state //TODO if null, standy by state 
 flag_t flag;
protected:
 uint8_t stepType;
};
typedef Step_t *StepPtr;

#endif

.ccp

#include <step/Step.h>
#include <Arduino.h>


Step_t::Step_t(uint8_t _stepType = 0) : stepType(_stepType) {

}

void Step_t::setAction(void (*_action)()){
 action = _action;
}

void Step_t::setTransition(transition_t (*_transition)()) { 
 transition = _transition; 
}

bool Step_t::transitionIsSet() const {
 return transition ? true : false;
}

bool Step_t::validate() const {
 //Serial.print(F("transition validation "));
 return (*transition)() ? true : false;
}

void Step_t::setNextStep(Step_t *_nextStep) { 
 nextStep = _nextStep; 
}

Step_t *Step_t::getNextStep() const {
 return nextStep;
}

bool Step_t::getFlag(uint8_t _flag) const {
 return flag.check(_flag) ? true : false;
}

void Step_t::setFlag(uint8_t x, bool logic) {
 flag.set(x, logic);
}

void Step_t::execute() { //execute the action and check for trasition
 Serial.println(F("transition mark set"));
 if(action) action();
 //Serial.println("execute action");
} 


uint8_t Step_t::getStepType() const {
 return stepType;
}

[EDIT]Sorry… miss my copy paste… :stuck_out_tongue:
sketch:

#include <debug_util.h>
#include <Grafcet.h>
#include <step/Step.h>

Grafcet grafcet();

bool readChar(char input){ //function to test condition
    if(Serial.available()){
    char readChar = Serial.read();
    if(readChar==input){
      Serial.print("read char: ");
      Serial.println(input);
      return true;
    }  
  }
  return false;
}

void emergencyStop(){
  Serial.println("Emergency stop  !!!  All system deenergized");
}

//------step 1-----------------------------

Step_t step1();
void functionStep1(){
  Serial.println("Function step 1 triggered");
}
transition_t readChar_f(){
  readChar('f');
}

//------step 2-----------------------------

Step_t step2();
void functionStep2(){
  Serial.println("Function step 2 triggered");
}
transition_t readChar_h(){
  readChar('h');
}

//------step 3-----------------------------
//Pulse pulse;

void function_pulse_4Hz(){
  Serial.println("pulse function at 4Hz");
}

transition_t readChar_p(){
  readChar('p');
}


//--------------

void setup() {

  Serial.begin(9600);
  Serial.println("system start");
  delay(1000);
  //grafcet.setEmergencyStop(emergencyStop);

  
  grafcet.initial()->setTransition(&readChar_f);//first condition to star
  //grafcet.initial()->setNextStep(&step1);

  //step1.setAction(functionStep1);
  //step1.setTransition(&readChar_h);
  //step1.setNextStep(&step2);

  //step2.setAction(functionStep2);
  //step2.setTransition(&readChar_p);
 // step2.setNextStep(&pulse);

  //pulse.setAction(function_pulse_4Hz);
  //pulse.setTransition(&readChar_f);
  //pulse.setNextStep(&step2);
}

void loop() {
  //grafcet.handler();
  //delay(500);
}

sketch:

I don't think so.

I think that it is wrong to try to hide the fact that you are using pointers. Lose all the typedef statements that define XxxxPtr as a Xxxx *.

edit my previous post.. sorry again.

Lose all the typedef statements

Ok. will rake you're advice. Do you think that can cause the linker error ???

nitrof:
edit my previous post.. sorry again.

Ok. will rake you're advice. Do you think that can cause the linker error ???

you are programming in C++, why go through all of those pointer machinations?

PaulS:
I think that it is wrong to try to hide the fact that you are using pointers. Lose all the typedef statements that define XxxxPtr as a Xxxx *.

Do you think that can cause the linker error

I put all the code in one directory, and changed the #include statements to reflect that.

I changed all the StepPtr instances to Step_t *.

I removed the useless parentheses on the Grafcet and Step_t constructor calls in the ino file.

I got:

Sketch uses 2572 bytes (8%) of program storage space. Maximum is 30720 bytes.
Global variables use 252 bytes (12%) of dynamic memory, leaving 1796 bytes for local variables. Maximum is 2048 bytes.

I put all the code in one directory, and changed the #include statements to reflect that.

Yes it fix it. I still remove pointer typedef like you said. adding parentheses to object declaration was while trying to fix the issue... removed as well !!

Out of curiosity, I've seen many library with file on sub folder... I wonder how they achieve it...

But thank a lot again for your time !

I wonder how they achieve it

Using very specific, supported, directory names.