Skip to content

Instantly share code, notes, and snippets.

@davidberard98
Last active January 24, 2023 04:03
Show Gist options
  • Save davidberard98/96b0b0e71b89406ddaba3e6849d434c0 to your computer and use it in GitHub Desktop.
Save davidberard98/96b0b0e71b89406ddaba3e6849d434c0 to your computer and use it in GitHub Desktop.
Explicit template specialization: demonstrating that the presence of a generic implementation can cause errors when an explicit template specialization exists. See run.sh for description
#pragma once
#include <stdexcept>
class Base {
public:
virtual void run() = 0;
};
template<int val>
class Derived final : public Base {
public:
#ifdef DEFINE_RUN
void run() override {
throw std::runtime_error("Unimplemented");
}
#else
void run() override;
#endif
};
#include "def.h"
int main() {
Derived<2> d;
d.run();
return 0;
}
Building a single artifact main_single
Running ./main_single (it should run fine):
> two
Build specialized_unimpl.o
Make it into a shared library specialized_unimpl.so
Build ./main_unimpl
Run ./main_unimpl
> two
Build specialized_impl.o
Make it into a shared library specialized_impl.so
Build ./main_impl
Run ./main_impl
terminate called after throwing an instance of 'std::runtime_error'
what(): Unimplemented
run.sh: line 20: 548969 Aborted (core dumped) LD_LIBRARY_PATH=`pwd` ./main_${SUFFIX}
# This file runs the test and has documentation.
# The general problem we were having is described below.
# We define
# class A{ virtual void run() = 0; };
# template<int val> class B : public A { void run() override { throw exception } };
# template<> B<2>::run() {cout << "implemented" << endl;} // and some other <3>, <4>, defined...
# Somehow, we call B<unknown>::run() and get the exception thrown.
# But when we remove the implementation of B::run (i.e. just leave it as `void run() override;`)
# we get "implemented". How is that possible? (i.e. how can we get an exception, but we don't have
# a compiler error when we remove the `{ throw exception }` definition?
# Turns out, we can repro this when we compile the `B<2>::run()` separately in a shared library...
# Below demonstrates this.
# def.h: header file containing (a) base class and (b) derived class implementation.
# Derived<2>::run() can return an exception or remain unimplemented depending on the scenario.
# specialized.cpp: contains a definition of an explicit template specialization for Derived<2>
# main.cpp: uses Derived<2>
### SINGLE ARTIFACT ###
echo "Building a single artifact main_single"
g++ main.cpp specialized.cpp -o main_single
echo -e "Running ./main_single (it should run fine):"
./main_single
echo -e '\n'
function build_shared () {
ARGS=$1
SUFFIX=$2
echo "Build specialized_${SUFFIX}.o"
g++ -c -fPIC specialized.cpp ${ARGS} -o specialized_${SUFFIX}.o
echo "Make it into a shared library specialized_${SUFFIX}.so"
g++ $ARGS -shared -o libspecialized_${SUFFIX}.so specialized_${SUFFIX}.o
echo "Build ./main_${SUFFIX}"
g++ -L`pwd` -lspecialized_${SUFFIX} main.cpp $ARGS -o main_${SUFFIX}
echo "Run ./main_${SUFFIX}"
LD_LIBRARY_PATH=`pwd` ./main_${SUFFIX}
}
### MULTIPLE ARTIFACT, DOES NOT HAVE DEFAULT IMPLEMENTATION OF RUN()
build_shared "" "unimpl"
echo -e '\n'
### MULTIPLE ARTIFACT, HAS DEFAULT IMPLEMENTATION OF RUN()
build_shared "-DDEFINE_RUN" "impl"
#include "def.h"
#include <iostream>
template<>
void Derived<2>::run() {
std::cout << " > two" << std::endl;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment