Skip to main content
edited body
Source Link
Roland Illig
  • 21.4k
  • 2
  • 34
  • 83
#include <cassert>
#include <string>
#include <vector>
#include <iostream>

int sign(int i)
{
    return i < 0 ? -1 : i > 0 ? +1 : 0;
}

template<typename T>
int spaceship(T a, T b)
{
    return a < b ? -123 : a > b ? +123 : 0;
}

std::string op(int cmp)
{
    return cmp < 0 ? "<" : cmp > 0 ? ">" : "==";
}

template<typename T>
void test_spaceship(const std::vector<T> &elements)
{
    bool error = false;

    for (std::size_t i = 0; i < elements.size(); ++i) {
        for (std::size_t j = 0; j < elements.size(); ++j) {
            int expected = spaceship(i, j);
            int actual = spaceship(elements[i], elements[j]);
            if (sign(expected) != sign(actual)) {
                std::cerr << __func__ << ":\n";
                std::cerr << "  expected " << elements[i] << " "
                          << op(expected) << " " << elements[j] << "\n";
                std::cerr << "  but got  " << elements[i] << " "
                          << op(actual) << " " << elements[j] << "\n";
                error = true;
            }
        }
    }
    std::flush(std::cerr);
    assert(!error);
}

int main()
{
    std::vector<std::string> elements{
            "",
            "first",
            "second",
            "zfourth", // intentionally in the wrong order
            "third",
            "zzfifth"
    };

    test_spaceship(elements);
    std::cout << "ok\n";
    std::string s;
    std::getline(std::cin, s);
}

My other favorite topic is the cctype header since you must never feed a plain character to functions like isalnum or toupper. Furthermore by doing this, you limit your program to 8-bit character sets, unless CHAR_MAXCHAR_BIT is greater than 8 on your machine, and chances are small for that.

#include <cassert>
#include <string>
#include <vector>
#include <iostream>

int sign(int i)
{
    return i < 0 ? -1 : i > 0 ? +1 : 0;
}

template<typename T>
int spaceship(T a, T b)
{
    return a < b ? -123 : a > b ? +123 : 0;
}

std::string op(int cmp)
{
    return cmp < 0 ? "<" : cmp > 0 ? ">" : "==";
}

template<typename T>
void test_spaceship(const std::vector<T> &elements)
{
    bool error = false;

    for (size_t i = 0; i < elements.size(); ++i) {
        for (size_t j = 0; j < elements.size(); ++j) {
            int expected = spaceship(i, j);
            int actual = spaceship(elements[i], elements[j]);
            if (sign(expected) != sign(actual)) {
                std::cerr << __func__ << ":\n";
                std::cerr << "  expected " << elements[i] << " "
                          << op(expected) << " " << elements[j] << "\n";
                std::cerr << "  but got  " << elements[i] << " "
                          << op(actual) << " " << elements[j] << "\n";
                error = true;
            }
        }
    }
    std::flush(std::cerr);
    assert(!error);
}

int main()
{
    std::vector<std::string> elements{
            "",
            "first",
            "second",
            "zfourth",
            "third",
            "zzfifth"
    };

    test_spaceship(elements);
    std::cout << "ok\n";
    std::string s;
    std::getline(std::cin, s);
}

My other favorite topic is the cctype header since you must never feed a plain character to functions like isalnum or toupper. Furthermore by doing this, you limit your program to 8-bit character sets, unless CHAR_MAX is greater than 8 on your machine, and chances are small for that.

#include <cassert>
#include <string>
#include <vector>
#include <iostream>

int sign(int i)
{
    return i < 0 ? -1 : i > 0 ? +1 : 0;
}

template<typename T>
int spaceship(T a, T b)
{
    return a < b ? -123 : a > b ? +123 : 0;
}

std::string op(int cmp)
{
    return cmp < 0 ? "<" : cmp > 0 ? ">" : "==";
}

template<typename T>
void test_spaceship(const std::vector<T> &elements)
{
    bool error = false;

    for (std::size_t i = 0; i < elements.size(); ++i) {
        for (std::size_t j = 0; j < elements.size(); ++j) {
            int expected = spaceship(i, j);
            int actual = spaceship(elements[i], elements[j]);
            if (sign(expected) != sign(actual)) {
                std::cerr << __func__ << ":\n";
                std::cerr << "  expected " << elements[i] << " "
                          << op(expected) << " " << elements[j] << "\n";
                std::cerr << "  but got  " << elements[i] << " "
                          << op(actual) << " " << elements[j] << "\n";
                error = true;
            }
        }
    }
    std::flush(std::cerr);
    assert(!error);
}

int main()
{
    std::vector<std::string> elements{
            "",
            "first",
            "second",
            "zfourth", // intentionally in the wrong order
            "third",
            "zzfifth"
    };

    test_spaceship(elements);
    std::cout << "ok\n";
    std::string s;
    std::getline(std::cin, s);
}

My other favorite topic is the cctype header since you must never feed a plain character to functions like isalnum or toupper. Furthermore by doing this, you limit your program to 8-bit character sets, unless CHAR_BIT is greater than 8 on your machine, and chances are small for that.

added 702 characters in body
Source Link
Roland Illig
  • 21.4k
  • 2
  • 34
  • 83

My other favorite topic is the cctype header since you must never feed a plain character to functions like isalnum or toupper. Furthermore by doing this, you limit your program to 8-bit character sets, unless CHAR_MAX is greater than 8 on your machine, and chances are small for that.

You should rather treat your strings as Unicode strings, and that brings a whole new topic of decisions, such as sorting strings from different scripts and languages. But that's still better than being caught in the 1990s with their limited code pages.


My other favorite topic is the cctype header since you must never feed a plain character to functions like isalnum or toupper. Furthermore by doing this, you limit your program to 8-bit character sets, unless CHAR_MAX is greater than 8 on your machine, and chances are small for that.

You should rather treat your strings as Unicode strings, and that brings a whole new topic of decisions, such as sorting strings from different scripts and languages. But that's still better than being caught in the 1990s with their limited code pages.

Source Link
Roland Illig
  • 21.4k
  • 2
  • 34
  • 83

My main suggestion for this code is for the helper function. It's not a good idea to only compare a few selected strings from the set of all interesting strings. It's far more efficient to just have a list of strings that are already ordered, and then ensure that this ordering is represented by the <=> operator under test.

To do this, each element should be compared to each element, including itself. The code I usually use to do this is:

#include <cassert>
#include <string>
#include <vector>
#include <iostream>

int sign(int i)
{
    return i < 0 ? -1 : i > 0 ? +1 : 0;
}

template<typename T>
int spaceship(T a, T b)
{
    return a < b ? -123 : a > b ? +123 : 0;
}

std::string op(int cmp)
{
    return cmp < 0 ? "<" : cmp > 0 ? ">" : "==";
}

template<typename T>
void test_spaceship(const std::vector<T> &elements)
{
    bool error = false;

    for (size_t i = 0; i < elements.size(); ++i) {
        for (size_t j = 0; j < elements.size(); ++j) {
            int expected = spaceship(i, j);
            int actual = spaceship(elements[i], elements[j]);
            if (sign(expected) != sign(actual)) {
                std::cerr << __func__ << ":\n";
                std::cerr << "  expected " << elements[i] << " "
                          << op(expected) << " " << elements[j] << "\n";
                std::cerr << "  but got  " << elements[i] << " "
                          << op(actual) << " " << elements[j] << "\n";
                error = true;
            }
        }
    }
    std::flush(std::cerr);
    assert(!error);
}

int main()
{
    std::vector<std::string> elements{
            "",
            "first",
            "second",
            "zfourth",
            "third",
            "zzfifth"
    };

    test_spaceship(elements);
    std::cout << "ok\n";
    std::string s;
    std::getline(std::cin, s);
}

Of course you would have to adjust the code a bit to test your Strong_String instead of my std::string, but the general idea should get clear.

If you have some strings that are considered equal by your operator <=>, you would have to adjust the above code to have a std::vector<std::vector<T>>, but that should be equally easy.