Skip to content

[Question] Is there a whay to assetEqual with struct/class parameters? #141

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
FedeBev opened this issue Jun 17, 2020 · 4 comments
Closed
Labels
question Further information is requested unittest libs The assert / assure / unittest reporting apparatus is affected

Comments

@FedeBev
Copy link

FedeBev commented Jun 17, 2020

Hi there,

I was wondering if it was possible to assert equality between structs/class instances, maybe the only requirement could be the definition of the == operator. If I'm not missing something, right now the operators < and `> must be implemented in order to assert equality.
For example:

struct definition:

struct ExampleStruct
{
    int myVal;

    bool operator==(const ExampleStruct &a) const
    {
        return x == a.x;
    }
};

test:

ExampleStruct a = {1};
ExampleStruct b = {2};

assertEqualObject(a, b);

Obviously, mostly when the struct is not very complex, something like this can be done:

ExampleStruct a = {1};
ExampleStruct b = {2};

assertEqual(a.myVal, b.myVal);

but when the struct is huge and/or with complex comparison logic could be tricky or error-prone to compare "manually" field by field. Moreover, in case of classes, private members can't be compared.

Thanks!

@ianfixes
Copy link
Collaborator

TL;DR check out Compare.h

The equality checks are all done using some macros adopted from the ArduinoUnit project. These work via the implementation of a "comparable" attribute, where as long as you can establish less-than or greater-than operations, everything else can be worked out.

So this should work:

struct ExampleStruct
{
    int myVal;

    bool operator > (const ExampleStruct &a) const 
    {
        return myVal > a.myVal;
    }

    bool operator < (const ExampleStruct &a) const 
    {
        return myVal < a.myVal;
    }

    /* not needed, but doesn't hurt
    bool operator==(const ExampleStruct &a) const
    {
        return myVal == a.myVal;
    }
    */
};

Although if I'm hearing your question correctly, you're worried about how multiple fields would figure into this. It should be no problem, you can chain the inequalities together in whatever order of priority makes sense. For example, say you wanted to do this for a struct that considers people's names (first, middle, last):

struct FullNameStruct
{
    String first;
    String middle;
    String last;

    bool operator > (const FullNameStruct &a) const
    {
        return last > a.last
            || first > a.first
            || middle > a.middle;
    }

//.... and so on

@FedeBev
Copy link
Author

FedeBev commented Jun 22, 2020

Thanks for your quick reply!

In order to make my question more clear, here is my struct declaration. Keep in mind that ParsedExpr is the result to be tested:

struct Range
{
    uint16_t from;
    uint16_t to;
};

template <typename T>
struct Array
{
    uint16_t size;
    T *values;

    Array()
    {
        values = nullptr;
    }

    ~Array()
    {
        if (values != nullptr)
            free(values);
    }
};

struct ExpValue
{
    bool each;
    Array<uint16_t> *list;
    Range *range;
    uint16_t *every;

    ExpValue()
    {
        list = nullptr;
        range = nullptr;
        every = nullptr;
    }

    ~ExpValue()
    {
        if (list != nullptr)
            delete list;

        if (range != nullptr)
            delete range;

        if (every != nullptr)
            delete every;
    }
};

struct ParsedExpr
{
    ExpValue *minute;
    ExpValue *hour;
    ExpValue *day;
    ExpValue *month;
    ExpValue *dayOfWeek;

    ParsedExpr()
    {
        minute = nullptr;
        hour = nullptr;
        day = nullptr;
        month = nullptr;
        dayOfWeek = nullptr;
    }

    ~ParsedExpr()
    {
        if (minute != nullptr)
            delete minute;
        if (hour != nullptr)
            delete hour;
        if (day != nullptr)
            delete day;
        if (month != nullptr)
            delete month;
        if (dayOfWeek != nullptr)
            delete dayOfWeek;
    }
};

I guess the only way to compare struct like that is to implement the operators for each (sub)struct, and that's fine and it is unavoidable.
However for some struct, such as Array or ExpValue, the operators < and > are really meaningless and can cause confusion, especially if someone else will use the library. When an array is greater or lower than other? Never, I guess... They can be bigger and with different values, but not greater.
Obviously is it possible to implement an "helper" comparison function and we can say a > b -> a != b; the following code is an implementation example for the simplest of my structs (please, don't focus on pointers and references, is just a concept):

struct Range
{
    uint16_t from;
    uint16_t to;

    bool isEqual(const Range a, const Range &b) const
    {
        if (a.from != b.from || a.to != b.to)
            return false;
        return true;
    }

    bool operator>(const Range &a) const
    {
        return !isEqual(*this, a);
    }

    bool operator>(const Range &a) const
    {
        return !isEqual(*this, a);
    }
};

But in my opinion that would be conceptually wrong even if it works for this specific use case; moreover it is way much tricky than a simple == operator, especially if that logic has to be propagated to each parent struct (and in this case has to be).

Am I wrong?
Thanks

@ianfixes
Copy link
Collaborator

When an array is greater or lower than other? Never, I guess... They can be bigger and with different values, but not greater.

I wouldn't say so. A String is an array of characters, and there is a very well-defined ordering of strings based on the values of the individual array elements (characters).

In the examples you posted, here is how I would do them:

bool operator > (const Range &a) const
{
    return a.to > b.to || a.from > b.from; // preference given to the maximum value
}

bool operator < (const Range &a) const
{
    return a.to < b.to || a.from < b.from; // preference given to the maximum value
}

As far as array inequality goes, just follow the example used by strcmp: https://www.techiedelight.com/implement-strcmp-function-c/

@FedeBev
Copy link
Author

FedeBev commented Jun 30, 2020

Sure, for String or char array I totally agree with you.
I understand what you are saying, and for some cases I can agree with you. But I still think this approach can be confusing in some situations (like mine).
I guess I'll deal with this using a comparison function "java-style".

However you kindly answered the question, I'm closing the issue.

Thanks! :)

@FedeBev FedeBev closed this as completed Jun 30, 2020
@ianfixes ianfixes added question Further information is requested unittest libs The assert / assure / unittest reporting apparatus is affected labels Sep 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested unittest libs The assert / assure / unittest reporting apparatus is affected
Projects
None yet
Development

No branches or pull requests

2 participants