0

I have a templated class where I'm overloading the addition and output operators and I have a particular specialization that is also templated. I haven't found any examples of how to do this and I end up with a linking error, so I'm left wondering if it's even possible.

// Point.hpp
namespace crypto {
// Forward declarations for template specialization
template<class T> class FieldElement;
template<class T> class Point;
template<class T> std::ostream& operator<<(std::ostream& out, const Point<T>& point);
template<class T> std::ostream& operator<<(std::ostream& out, const Point<FieldElement<T>>& point);
template<class T> Point<T> operator+(const Point<T>& lhs, const Point<T>& rhs);
template<class T>
Point<FieldElement<T>> operator+(const Point<FieldElement<T>>& lhs, const Point<FieldElement<T>>& rhs);

template<class T> class Point
{
  public:
    Point(std::optional<T> x, std::optional<T> y, T a, T b);
    ~Point() = default;

    template<class U> friend Point operator+(const Point& lhs, const Point& rhs);
    template<class U> friend std::ostream& operator<<(std::ostream& os, const Point<U>& element);

    std::optional<T> X;
    std::optional<T> Y;
    T A;
    T B;
};

template<class T> Point<T> operator+(const Point<T>& lhs, const Point<T>& rhs)
{
    ...
}

template<class T> std::ostream& operator<<(std::ostream& os, const Point<T>& point)
{
    // Write Point to stream
    if (!point.X)
        os << "Point(infinity)";
    else
        os << "Point(" << point.X.value() << "," << point.Y.value() << ")_" << point.A << "_" << point.B;

    return os;
}
}

And the specializations:

// Point.cpp
namespace crypto {

template<class T> Point<FieldElement<T>> operator+(const Point<FieldElement<T>>& lhs, const Point<FieldElement<T>>& rhs)
{
    ...
}

template<class T> std::ostream& operator<<(std::ostream& os, const Point<FieldElement<T>>& point)
{
    if (!point.X)
        os << "Point(infinity)";
    else
    {
        os << "Point(" << point.X.value().Number << "," << point.Y.value().Number << ")_" << point.A.Number << "_"
           << point.B.Number << " FieldElement(" << point.X.value().Prime << ")";
    }

    return os;
}
}

The calling code:

// Test.cpp
TEST(IntegrationTests, PointAdditionTests)
{
    auto prime = 223;
    auto a = FieldElement(0, prime);
    auto b = FieldElement(7, prime);
    auto x1 = FieldElement(192, prime);
    auto y1 = FieldElement(105, prime);
    auto x2 = FieldElement(17, prime);
    auto y2 = FieldElement(56, prime);
    auto p1 = Point<FieldElement<int>>(x1, y1, a, b);
    auto p2 = Point<FieldElement<int>>(x2, y2, a, b);
    std::cout << p1 + p2 << std::endl;
}

And finally my linking error: clang++ -o buil

d/bitcoin/crypto/CryptoLibTests -pthread build/bitcoin/crypto/tests/CryptoIntegrationTests.o build/bitcoin/crypto/tests/FieldElementTests.o build/bitcoin/crypto/tests/PointTests.o -Lbuild/lib -lpthread -lBtcCrypto -lgtest -lgtest_main
Undefined symbols for architecture x86_64:
  "std::__1::basic_ostream<char, std::__1::char_traits<char> >& crypto::operator<<<int>(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, crypto::Point<crypto::FieldElement<int> > const&)", referenced from:
      IntegrationTests_PointAdditionTests_Test::TestBody() in CryptoIntegrationTests.o
  "crypto::Point<crypto::FieldElement<int> > crypto::operator+<int>(crypto::Point<crypto::FieldElement<int> > const&, crypto::Point<crypto::FieldElement<int> > const&)", referenced from:
      IntegrationTests_PointAdditionTests_Test::TestBody() in CryptoIntegrationTests.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
scons: *** [build/bitcoin/crypto/CryptoLibTests] Error 1
scons: building terminated because of errors.
Aginor
  • 178
  • 5
  • 18
  • The specializations are only available in Point.cpp. See [Why can templates only be implemented in the header file?](https://stackoverflow.com/q/495021/1553090) – paddy Nov 09 '21 at 02:37
  • Well, what do you know, that worked! Thank you, @paddy – Aginor Nov 09 '21 at 04:02

1 Answers1

1

Thanks to @paddy, the answer was to move the specialization into the header file. The above implementation works then

Aginor
  • 178
  • 5
  • 18