Template without changing function signature

Is there a way to change this to a templated function. From what I read so far, templated functions vary based on the type. I need to come up with a single template for these two functions and their future counterparts for left and right sonar. They need to take in an int and return an int. I want to know if I can somehow specify on the template invocation the value of a parameter such as int Sonar_Type::front or int Sonar_Type::back and have the proper function instantiated. Based on Sonar_Type, there would be a different comparator, trigger pin for the HC-sR04, Kalman filter and modifying a different global variable such as front_sonar_distance or rear_sonar_distance. I need to make the function signature remain as I am using function pointers as parameters to constructors in Behavior class.

Thanks,
Chris

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
  int Robot::check_rear_sonar( int flag=0) {
        //std::cout << "Robot::Check Rear Sonar : START | with param " << flag << std::endl;
        int rcounts=0;
        int comparator=0;
        if (flag == 0 )  //just an absolute checker
            comparator=127; //3 inches
        else
            comparator=(-1*flag);    //convert to positive
        
        if (native) {
            do {
               rcounts++;
               //Send trig pulse
               digitalWrite(HC_TRIG_R, HIGH);
               delayMicroseconds(20);
               digitalWrite(HC_TRIG_R, LOW);
 
               //Wait for echo start
               while(digitalRead(HC_ECHO_R) == LOW);
 
               //Wait for echo end
               long startTime = micros();
               while(digitalRead(HC_ECHO_R) == HIGH);
               long travelTime = micros() - startTime;
 
               //Get distance in mm
               rear_sonar_distance = (travelTime / 5.8) ;
               
               //std::cout <<"RS : " << rear_sonar_distance << std::endl;  
               usleep(500000);
            } while ((rear_sonar_distance < 30) && (rcounts <10 )); //unreliable less 3 inches
              
            //high confidence on the result if rcounts < 10
            if (rcounts < 10)
               Rf.set(Point3d(0,rear_sonar_distance,0));  //Kalman filter
            //obtain from the filter to smooth out errant values
               rear_sonar_distance=Rf.get().y-30; 
               //std::cout << "RSF : rear_sonar_distance " << rear_sonar_distance << std::endl;
               
            
            if (rear_sonar_distance < comparator ) {
               //std::cout << "Robot::Check Rear Sonar : END =  FALSE" << std::endl;
               //std::cout << "CRS: false due to flag of " << comparator << " is greater than " << rear_sonar_distance << " or rcounts is == 10 at " << rcounts<<  std::endl;
               return false;
            } else {
               //std::cout << "Robot::Check Rear Sonar : END = TRUE" << std::endl;
               return true;
            }
        } else { //no rear sonar
              rear_sonar_distance=1000;
              //std::cout << "Robot::Check Rear Sonar : END = TRUE" << std::endl;
              return true;
        }
              
    }
    
int Robot::check_front_sonar(int flag=0) {
        //std::cout << "Robot::Check Front Sonar : START " << std::endl;
        int fcounts=0;
        int comparator=0;
        if (flag == 0 )  //just an absolute checker
            comparator=180; //5 inches (2 inch offset from edge of robot)
        else
            comparator=flag;  
        if (native) {
            do {
                
              fcounts++;           
              //Send trig pulse
              digitalWrite(HC_TRIG, HIGH);
              delayMicroseconds(20);
              digitalWrite(HC_TRIG, LOW);
 
              //Wait for echo start
              while(digitalRead(HC_ECHO) == LOW);
 
              //Wait for echo end
              long startTime = micros();
              while(digitalRead(HC_ECHO) == HIGH);
              long travelTime = micros() - startTime;
 
              //Get distance in mm
              front_sonar_distance = (travelTime / 5.8);
              //std::cout <<"FS : " << front_sonar_distance << std::endl;
              usleep(500000);
            } while ((front_sonar_distance < 30) && (fcounts <10 ));  //unreliable less 3 inches
            
            if (fcounts < 10)
               Ff.set(Point3d(0,front_sonar_distance,0)); //Kalman filter
            
               front_sonar_distance=Ff.get().y-30; 
               //std::cout << "FSF : front_sonar_distance " <<  front_sonar_distance << std::endl;
            
              
            if (front_sonar_distance < comparator){
                //std::cout << "Robot::Check Front Sonar : END " << std::endl;
                //std::cout << "CFS: false due to flag of " << comparator << " is greater than " << front_sonar_distance << " or fcounts is == 10 at " << fcounts<<  std::endl;
                return false;
            } else {
                //std::cout << "Robot::Check Front Sonar : END " << std::endl;
                return true;
            }
        } else {  //NO front sonar
              front_sonar_distance=1000;
              //std::cout << "Robot::Check Front Sonar : END " << std::endl;
              return true;
         }
              
    }
    
i'm not sure if i understood your problem correctly. do you want to use a certain function depending on the input. i would just use a switch statement if your Sonar_Type is an enum.

Your returning a boolean but your expecting an integer value the result you require is it just a 1 and 0?

let me know if its any help.


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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class Robot
{
public:
	int check_rear_sonar(int flag = 0)
	{
		return 50; 
	}

	int check_front_sonar(int flag = 0)
	{
		return 1000;
	}

};

enum Sonar_Type { Front = 1, Back };

int main()
{
	Robot robot;

	// depending on your input parameters you get to use a different method.
	int val = Back;
	//int val = Front;

	int output;

	switch (val)
	{
		case Front:
		{
			output = robot.check_front_sonar(0);			
		}
		break;

		case Back:
		{
			output = robot.check_rear_sonar(0);
		}
		break;
	}

	std::cout << "input is " << val <<  " output is " << output;

	int x;
	cin >> x;
}
Last edited on
I have a polymorphic Behavior class that has MacroBehaviors and MicroBehaviors derived from it. I am assiging function pointers to each MicroBehavior. Each microbehavior when it executes, calls the function it is assigned. An executing macrobehavior ( which is built from a group of microbehaviors) simply loads the first microbehavior in its class . The functions live in Robot class. Each microbehavior/macrobehavior executing in turn puts the "next behavior" in the running deque. Each Behavior is "programmed" in the sense that it knows its "next_behavior" based on who called it (preceeding behavior that executed ) and wether the line of executing behaviors have been successful or has had some failure (enum run_type is F or S to designate this ) and wether this executing behavior succeeds or fails ( enum result type is f or s ). A 2x2 matrix with values for F,S,f,s decides the next_behavior. So to make things really modular, I need the function pointers to have the same signature. But in case, I need to derive some value from each Behavior to influence a succeeding Behavior, I decided to use an int over a boolean. Figuring that due to implicit cast from bool to int, If I have a zero value, it will be bool(0) which is false and a nonzero value will be bool(-5) which evaluates to true but I get additional information( -5) to pass around if I need to. This might be "bad practice", I am not sure. So, I am trying to move away from global variables in Robot class or minimize the use.
The check_rear_sonar will work in two modes: Given an input of 0 or nothing, it simply tells me that there is an obstacle with 5 inches in front of it. If I give it a distance input ( like how far the robot needs to travel ), it will tell me if there is an obstacle before getting there. I wish to use it as a Check_Sonar Behavior which will output true or false thereby influencing wether Move Behavior is called or Idle Behavior for example.
I was wondering if the following pseudo code will even work:

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
27
28
29
typedef int(Robot::*m_behavior)(int); //the function signature

//Constructor for Check_Sonar MicroBehavior:

Check_rear_Sonar::Check_rear_Sonar(Robot &o, std::string iid) : MicroBehavior(o, &Robot::check_sonar<rear_sonar>, iid) { m_behavior=&Robot::check_sonar<rear_sonar>};  //does assigning a templated function maintain the function signature so a variable of type m_behavior *  can be assigned an address ?

Check_front_Sonar::Check_front_Sonar(Robot &o, std::string iid) : MicroBehavior(o, &Robot::check_sonar<front_sonar>, iid) { m_behavior=&Robot::check_sonar<front_sonar> }; 


//Robot check_sonar pseudo function, it is based on the value of sonar_type, instead of its type.
template <sonar_type> int check_sonar(int){
if sonar_type == rear {
     filter = Rf;
    comparator = 30;
}

if sonar_type == front {
   filter = Ff;
   comparator = 60;
}
{ 

//the rest of the similar code


}
return result; 
}


I need to know at the creation of the Behavior object what the exact address is of the template instance that applies to Check_front_sonar and Check_rear_sonar. I cannot use the function parameter to designate if I wish to check the front sonar or the rear as I am using that input for other things. I dont want the function to depend on a global variable. But perhaps the <> can serve as another parameter and at the same time reduce code duplication?

What I am really trying to achieve is to reduce the code duplication with a template that instantiates functions based on a value instead of a type so there is only one Check_Sonar template. I dont even know if it is possible. I am still doing my research but I thought I'd ask the forum. I have learned quite a bit already from this forum within just the past couple of weeks. Ultimately, my ideas on how my project should work may change again. Truly, I have no idea if I can finish it in the manner that I envision currently. But it has served as a vehicle to keep me interested in studying cpp.

Thanks,
Chris


I think I figured it out:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <cstdlib>
#include <iostream>
using namespace std;

enum Sonar_Type {
    Rear,
    Front
};
template <Sonar_Type type> 
int check_sonar(int t) {
    
    int comparator=0;
    if (type==Rear) {
        std::cout << "You called the rear sonar" << std::endl;
        comparator=30;
    }
    if (type==Front) {
        std::cout << "You called the front sonar" << std::endl;
        comparator=20;
    }
    
    //common code
    return  comparator*t;
}

int check_me(int t){
    std::cout << "Checking " << std::endl;
}

typedef int(*m_behavior)(int);



/*
 * 
 */
int main(int argc, char** argv) {
    m_behavior  fptr;
    fptr=&check_me;  
    fptr=&check_sonar<Rear>;    //function instance conforms to m_behavior
    std::cout << check_sonar<Rear>(0) << std::endl;
    std::cout << fptr(4) << std::endl;
    std::cout << check_sonar<Front>(1) << std::endl;
    return 0;
}



Thanks
Chris
why not pass a structure with all the information in it. instead of limiting yourself to an int. you can put all the data you need that that point in time.
I dont really know the data I will need to pass around at this point. I know for sure that they all need to return a bool true or false because that is integral to determining the next_behavior. . But in future, a bigger data payload may have to be returned. The problem is, each function may need to return something unique. Plan_Navigation returns a vector of floats, for example, that has to be global for now. Screen_Update will need to receive a Point3d location and Heading which are both global also for now. So I have been thinking about void pointers as return values. A null void pointer could be treated as a boolean false and a pointer with a valid address as a boolean true. Then each behavior will accept a pointer parameter, and need to cast it to a specific type it can process and obtain the value or values. I'm not sure of the mechanics of this yet but it seems that it would be possible. But this also implies that information needs to be able to be directed from source Behavior to target Behavior and that relationship needs to be explicit, as a Screen_Update behavior cant process a vector of floats from Plan_Navigation. Right now I am passing around ints via an "email" system. There is a "programming stage" in each Behavior object where the transitions are set (in the body of the constructor). Then a queue building stage where the Behaviors are loaded into the run_stack (called when the Behavior->exec is called) . During the "programming" stage, Behavior A sets that it needs to send email to Behavior B. Behavior B "subscribes" to Behavior A's maillist. When Behavior A executes, it sends the result to Behavior B's mailbox. Behavior B may not be the next_behavior but is expected to be queued at some point after Behavior A executes. When Behavior B executes, it checks its "mailbox" for the value of the int that it uses as a parameter to change the way it executes. Sorry for the extended response. I'm trying to flesh out some ideas. I am obviously open to suggestions.

Thanks
Chris
Topic archived. No new replies allowed.