I'm trying to make a function accept different arguments depending on the enum.
// cake.h
#pragma once
#include <utility>
enum class TYPE { CupCake, Jelly, BirthdayCake };
struct cake_tin { /* etc etc */ };
/** Fills in different ingredients for different types of cake */
template <TYPE, typename ...Args>
void fill_tin(cake_tin &tin, Args... args);
/** Bakes a cake */
template <TYPE type, typename ...Args>
void bake_cake(Args&&...args) {
cake_tin tin;
fill_tin<type>(tin, std::forward<Args>(args)...);
}
.cpp file contains the specialisations of the function
// cake.cpp
#include "cake.h"
// If I put this in cake.h, I get multiple definition error (since the template's fully specalised), but if I move this to the .cpp file, I get undefined reference when I compile
template <>
void fill_tin<TYPE::CupCake>(cake_tin &tin, int cherries) {
// ... etc etc
}
template <>
void fill_tin<TYPE::Jelly>(cake_tin &tin, bool wobble) {
// ... etc etc
}
For completeness
// main.cpp
#include "cake.h"
int main() {
bake_cake<TYPE::CupCake>(1);
}
Another file that includes cake.h
// other.cpp
#include "cake.h"
Compile with clang++ -Wall -Wextra -std=c++11 main.cpp other.cpp -o main
I've tried putting in declarations
template <> void fill_tin<TYPE::CupCake>(cake_tin &tin, int cherries);
template <> void fill_tin<TYPE::Jelly>(cake_tin &tin, bool wobble);
and still can't get that template instantiated. If I move the specialisations into the header then they'll cause redefinition when the header is included in multiple compilation units at the link stage.
My question is: How can I get this to work, or is there a better way of doing this?
Trying to mix template specialization with function overloading is always going to be horribly painful.
Either provide class template specializations:
template<TYPE> struct tin_filler; // undefined
template<> struct tin_filler<TYPE::CupCake> {
void operator()(cake_tin& tin, int cherries); };
template<> struct tin_filler<TYPE::Jelly> {
void operator()(cake_tin& tin, bool wobble); };
template <TYPE type, typename ...Args>
void bake_cake(Args&&...args) {
cake_tin tin;
tin_filler<type>()(tin, std::forward<Args>(args)...);
}
Or use integral_constant to reify the TYPE and use normal function overloading:
void fill_tin(std::integral_constant<TYPE, TYPE::CupCake>, cake_tin& tin, int cherries);
void fill_tin(std::integral_constant<TYPE, TYPE::Jelly>, cake_tin& tin, bool wobble);
template <TYPE type, typename ...Args>
void bake_cake(Args&&...args) {
cake_tin tin;
fill_tin(std::integral_constant<TYPE, type>(), tin, std::forward<Args>(args)...);
}
The former is more general, but the latter could well be clearer and simpler in this case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With