Trying to avoid repeating myself and global variables. How would I write something that can be called to filter multiple inputs? |
Replace the static variable with a parameter managed by the caller.
1 2 3 4 5 6
|
float make_filter_context() { return 0.f; }
float lpfilter(float* ctx, float input, float coef)
{
return *ctx += (input - *ctx) * coef;
}
|
To be used as
1 2
|
float context = make_filter_context();
std::cout << lpfilter(&context, analog_input_value(), .045f);
|
The variable
context represents the state of the filter. You can use this method to filter multiple sequences, as long as you use a separate context variables for each sequence -- and thereby keep their states distinct.
If you're implementing some filter which needs to recall the last ten values and a coefficient, you would generally bundle this data up, define a function to set its initial values, and apply the same idiom:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
struct filter_context
{
float history[10];
float coef;
};
// create a filter_context and set its initial state
filter_context make_filter_context()
{
filter_context x;
for (float& elt: x.history) elt = 0.f;
x.coef = 0;
return x;
}
// apply the filter whose state is defined by *ctx to the input
float magic_filter(filter_context* ctx, float input);
int main()
{
filter_context context = make_filter_context();
std::cout << magic_filter(&context, analog_input_value()) << '\n';
}
|
In this example,
filter_context exists only to bundle related data together. It simply holds all the filter's state, and gives the filter function a place to store it for next time. This is a common idiom in both C and C++: the class exists to bundle data together so it can be handled more readily as a unit.
The prior example is both a C program and a C++ program. The C++ solution is a bit better, but the essence of the solution is already here:
1 2 3 4 5 6 7 8 9 10 11 12 13
|
struct filter_context
{
std::array<float, 10> history{};
float coefficient = 0.f;
};
float magic_filter(filter_context& ctx, float input);
int main()
{
filter_context context;
std::cout << magic_filter(&context, analog_input_value()) << '\n';
}
|
Here
in-class initializers are used to eliminate the handwritten
make_filter_context function, and a reference parameter is used to indicate that a null pointer is not an acceptable argument to
magic_filter.
The vocabulary is confusing |
C++'s terminology is
somewhat different from that of other communities. There is a precise C++-specific terminology, which I use in all my answers here, and a generic terminology for object-oriented programming, which I try to avoid.
I've avoided structs and classes because I don't really understand them. I don't know how the variable's (object?) lifetime work |
The lifetime of an object is unrelated to its type. For example, whether an object has fundamental type (e.g.,
float) or class type (e.g.,
filter_context) doesn't make any difference. You may treat
filter_context like a
float -- at least as far as its lifetime is concerned.
Methods and member functions are the same thing, right? They seem to be used interchangeably |
Method is a generic term for
member functions.