0

When trying to compile the files bellow this error occurs:

The error

Logging.h: In instantiation of 'void Sudoku::printBoardWithCandidates(const Sudoku::Board<BASE>&) [with int BASE = 3]':
Logging.h:10:64:   required from here
Logging.h:10:64: error: explicit instantiation of 'void Sudoku::printBoardWithCandidates(const Sudoku::Board<BASE>&) [with int BASE = 3]' but no definition available [-fpermissive]
     template void printBoardWithCandidates<3>(const Board<3>& b);
                                                                ^

As I am not very experienced in C++ I don't see any possible cause for this problem. Since the definition is present in .cpp file I dont see any reason not to compile. Wouldn't someone mind explaing that to me, please?

.h file

#pragma once
#include "Board.h"
namespace Sudoku
{
    template<int BASE>
    void printBoardWithCandidates(const Board<BASE> &b);


    template void printBoardWithCandidates<2>(const Board<2>&);
    template void printBoardWithCandidates<3>(const Board<3>&);
    template void printBoardWithCandidates<4>(const Board<4>&);
} 

.cpp file

#include "Logging.h"
#include <iostream>

template<int BASE>
void Sudoku::printBoardWithCandidates(const Board<BASE> &board)
{
    // definition...
}

Edit: Similar implementatiin I have employed several times throughout the program. For example Board.h

#pragma once
#include <vector>
#include <cstring>
#include "stdint.h"

namespace Sudoku
{
    struct Cell
    {
        int RowInd;
        int ColInd;
        int BoxInd;

        friend bool operator==(const Cell& cell1, const Cell& cell2);
    };
    
    bool operator==(const Cell& cell1, const Cell& cell2);


    template<int BASE>
    class Board
    {
        public:
            static constexpr int WIDTH = BASE * BASE;
            static constexpr int CELL_COUNT = WIDTH * WIDTH;
            static constexpr uint16_t CELL_COMPLETELY_OCCUPIED = 65535 >> (sizeof(uint16_t) * 8 - WIDTH);

        private:   
            const int EMPTY_VALUE;
            
            uint16_t rowOccupants[WIDTH] = {0};
            uint16_t colOccupants[WIDTH] = {0};
            uint16_t boxOccupants[WIDTH] = {0};

            int* solution;
            void Init();
            void Eliminate(Cell& cell, uint16_t value);

        public:


            std::vector<Cell> EmptyCells;
            Board(const int* puzzle, int* solution, int emptyValue = -1);
            void SetValue(Cell cell, uint16_t value);
            void SetValue(Cell cell, int value);
            int* GetSolution() const; 
            inline uint16_t GetOccupants(Cell cell) const
            {
                return rowOccupants[cell.RowInd] | colOccupants[cell.ColInd] | boxOccupants[cell.BoxInd];
            }

    };
    template class Board<2>;
    template class Board<3>;
    template class Board<4>;
} // namespace Sudoku
Pepa Zdepa
  • 71
  • 1
  • 5
  • 1
    Does this answer your question? [Why can templates only be implemented in the header file?](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – paolo May 17 '22 at 14:21
  • @paolo I don't think it does. It mentions explicit instantions, but not the error in question. – Yksisarvinen May 17 '22 at 14:26
  • @paolo thank you for sharing that, but I am afraid it doesn't ressolve this. Especially when one considers the implementation above has worked for me fine when working with template classes (such as mentioned Board). – Pepa Zdepa May 17 '22 at 14:34
  • @PepaZdepa Why exactly are you using explicit instantiation here? It only really makes sense for very particular purposes: To reduce the compilation time and/or to reduce dependencies between header files. Otherwise just put all template definitions in the header and you won't need any explicit instantiations at all. That's generally the default approach for templates. The explicit instantiations you have written in the question do not serve any purpose. – user17732522 May 17 '22 at 15:28

1 Answers1

1

The problem is that at the point inside the header file where you have provided the 3 explicit template instantiation, the definition of the corresponding member function template printBoardWithCandidates is not available. Thus, the compiler cannot generate the definition for these instantiations and gives the mentioned error saying:

error: explicit instantiation of 'void Sudoku::printBoardWithCandidates(const Sudoku::Board<BASE>&) [with int BASE = 3]' 
but no definition available [-fpermissive]
^^^^^^^^^^^^^^^^^^^^^^^^^^^

There are two ways to solve this as shown below. Note also that i have used an empty struct Board since the Board class you provided is very big(in length) and will take a lot of space if pasted here 2 times. The concept is the same though.

Method 1

Provide the definition for the member function template printBoardWithCandidates in the header before providing the 3 explicit template instantiations as shown below:

header.h

#pragma once
#include "Board.h"
namespace Sudoku
{
    template<int BASE>
    void printBoardWithCandidates(const Board<BASE> &b)
    {
        //note this is a definition
    }

    //explicit template instantiation declaration
    extern template void printBoardWithCandidates<2>(const Board<2>&);
    extern template void printBoardWithCandidates<3>(const Board<3>&);
    extern template void printBoardWithCandidates<4>(const Board<4>&);
} 

Board.h

#pragma once
template<int>
struct Board 
{
    
};

main.cpp


#include <iostream>

#include "header.h"
//explicit template instantiation definition
template void Sudoku::printBoardWithCandidates<2>(const Board<2>&);
template void Sudoku::printBoardWithCandidates<3>(const Board<3>&);
template void Sudoku::printBoardWithCandidates<4>(const Board<4>&);
int main()
{
    
    return 0;
}

Working demo


Method 2

Here we provide the definition of the member function template as well as the 3 explicit template instantiations in the source file. In the header file we only provide the declaration for the member function template and no explicit template instantiation are provided in the header file.

header.h

#pragma once
#include "Board.h"
namespace Sudoku
{
    //this is a declaration not a definition
    template<int BASE>
    void printBoardWithCandidates(const Board<BASE> &b);


   //no explicit template instantiation here since we have provided only the declaration above and not the definition
} 

Board.h

#pragma once
template<int>
struct Board 
{
    
};

source.cpp

#include "header.h"

//provide the definition here
template<int BASE>
void Sudoku::printBoardWithCandidates(const Board<BASE> &board)
{
    // definition...
}
 template void Sudoku::printBoardWithCandidates<2>(const Board<2>&);
    template void Sudoku::printBoardWithCandidates<3>(const Board<3>&);
    template void Sudoku::printBoardWithCandidates<4>(const Board<4>&);

main.cpp

#include <iostream>

#include "header.h"

int main()
{
    
    return 0;
}

Working demo

Jason
  • 36,170
  • 5
  • 26
  • 60