0

Unfortunately I have yet to learn C++ though I have experience with C# and other non-C languages. After trying for the past 5 hours to pick up what I can from Stack and other sites I haven't found a workable solution that actually works with Arduino IDE (hence why I'm posting here and not stackoverflow).

Im trying to create a simple library to handle some analog inputs. I need to be able to compare the value from a pin to a list of values and handle the result accordingly. I can easily send the number of the pin to the constructor and read it. My issue is actually defining that list from the setup() function.

The list can be variable in length so I can't hard code an array in the library class and then pass values one by one. I can pass the length in the constructor but then I'm not really sure how to use it to initialize the array length without having the values to assign.

In C# or other languages I'd either use a list or pass the array directly. Neither of these seem to be an option with C++ (or at least I couldn't pass the array correctly). How could I solve this problem? If it helps I'm using a teensy++ 2.0.

3
  • I've chosen to undelete your question because there doesn't seem to be a reason for deletion. If you feel this was a mistake, feel free to reply to this comment. Thanks! Aug 4, 2015 at 15:26
  • @jimjimmy1995 - please don't delete a question once you have an answer to it. The question and answer are intended to help other people in the future with similar problems. Thanks!
    – Nick Gammon
    Aug 4, 2015 at 20:37
  • I deleted it before there were any answers as I realized my mistake when trying to pass the array. I no longer needed to ask the question and the solution was more fitting of stackoverflow. Aug 5, 2015 at 19:32

3 Answers 3

2

If the list is a static list you could conceivably pass a pointer to it in the constructor. Example:

class myClass {
  const int num_;
  const int * values_;

  public:

  // constructor
  myClass (const int num, const int * values) : num_ (num), values_ (values) { } 

  // other stuff here

  // demo
  void printThem ();
};  // end of class myClass

void myClass::printThem ()
  {
  for (int i = 0; i < num_; i++)
    Serial.println (values_ [i]);  
  } // end of myClass::printThem

const int NUMBER_OF_VALUES = 6;
const int myValues [NUMBER_OF_VALUES] = { 22, 33, 44, 55, 66, 77 };

// make an instance of the class with the wanted values
myClass foo (NUMBER_OF_VALUES, myValues);

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();

  foo.printThem ();
  }  // end of setup

void loop ()
  {

  }  // end of loop

That is the simple method. The values in the list are passed to the constructor as a pointer, the class remembers the list and uses it when necessary.

2

Here are a few examples how to pass vectors and variable number of arguments to a function in C/C++:

void foo(int* vector, size_t nmemb);

The function foo takes a vector of integers and the number of members, nmemb.

const size_t VECTOR_MAX = 5;
int vector[VECTOR_MAX] = { 0, 1, 3, 5, 7 };

foo(vector, VECTOR_MAX);

The next variant is generalization. A vector of whatever, number of members, and size of each member;

void fie(void* vector, size_t nmemb, size_t size);

This allows the function to handle different types of element.

fie(vector, VECTOR_MAX, sizeof(int));

Examples of usage are the standard library functions qsort() and bsearch().

The next variant is variable arguments. C/C++ allows the syntax "..." for a list of arguments.

void fum(size_t nmemb, ...);

This allows the following call:

fum(5, 0, 1, 3, 5, 7);

The implementation of function must be able to "parse" the argument list.

#include <stdarg.h>

void fum(size_t nmemb, ...)
{
  va_list ap;
  va_start(ap, nmemb);
  int arg;
  for (size_t i = 0; i < nmemb; i++) {
    va_arg(int, arg);
    ...
  }
  va_end(ap);
}

There are four primary methods to determine the number of arguments and value types; 1) number of arguments as parameter and fixed data type, 2) special end parameter value, 3) indirect through format descriptor such as printf() format string, and 4) attribute-value pairs.

Below is an example of variable number of arguments as attribute-value pairs.

void set_attrs(size nr, ...);

set_attrs(5, X_POS, 10, Y_POS, 110, R_COLOR, 0, G_COLOR, 255, B_COLOR, 0); 

A usage of this notation can be found in Motif.

2

Arrays in C/C++ are handled as a pointer. That means the value of the pointer is the location in memory of the first element in the array. The next element is at that starting address, plus the size of each element. So for an array of 1-byte characters, each item in the array is at the very next address. For 32-bit members, it's at +4, and so forth.

You're storing an array of values to compare with analogRead() - so uint16_t (aka: short int) will be large enough for each element, without wasting too much memory. But we also need to store a count of them, I'll assume 32-bits is enough readings.

uint16_t *comparison_points;
uint32_t  comparison_point_count;

These can be declared statically like:

uint16_t  comparison_points[] = { 2, 4, 6, 8, 47 };
uint32_t  comparison_point_count = 5;

Giving you a comparison function like:

bool readingInSet(uint16_t reading, uint16_t *check_points, uint32_t check_points_length)
{
    bool found = false;
    for (uint32_t i=0; i<check_points_length; i++)
    {
        if (reading == check_points[i])
        {
            found = true;
            break;
        }
    }
    return found;
}

But you could also make the array into an object, embedding this functionality

class AnalogueVector
{
public:
    AnalogueVector(uint32_t size, uint16_t *points)
    {
        comparison_point_count = size;
        comparison_points = points;
    }

    bool readingIn(uint16_t reading)
    {
        bool found = false;
        for (uint32_t i=0; i<comparison_point_count; i++)
        {
            if (reading == comparison_points[i])
            {
                found = true;
                break;
            }
        }
        return found;
    }

private:
    uint16_t *comparison_points;
    uint32_t  comparison_point_count;
};

Giving a usage like:

uint16_t  comparison_points[] = { 2, 4, 6, 8, 47 };
uint32_t  comparison_point_count = 5;

AnalogueVector cmp_points(comparison_point_count, comparison_points);

if (cmp_points.readingIn(analogRead(A1)))
    Serial.println("Matched");
else
    Serial.println("no");

Obviously if you're reading these data points in from a file, or other external media, then you would need to allocate space for the items, and store them in the object.

As with most coding problems, there's multiple solutions. Here I've tried to keep it simple, and explore different ways of handling array data, without getting to far away from that fact that an array in C/C++ is just a big contiguous block of data in memory somewhere. The address of that data, (aka the pointer), is simply the address of that first element.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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