0

I am a c++ beginner and this is my first time to write c++ in a mac environment using clang++, I had make sure clang++ had been used correctly by compile a simple program, and it runs perfectly.

The problem is I cannot compile the program in mac environment after separating the files to multiple files and using include to link them, as well as trying to implement OOP to my sudoku and link 2 class together.

So I believe there might be something wrong related to the linker or my OOP method.

I found this article might be useful but I had tried to follow the recommend method but seems doesn't work:

https://stackoverflow.com/questions/23179099/undefined-symbols-for-architecture-x86-64copy-constructor

https://stackoverflow.com/questions/74086956/c-macos-m1-ld-symbols-not-found-for-architecture-arm64-clang-error

https://stackoverflow.com/questions/73864774/ld-symbols-not-found-for-architecture-arm64-c

There are some other information in the internet related to "I defined a function / variable in header files but did not implemented it in the .cpp files", I don't think this is the case for me. As all defined function in header files has been implemented already. There is a few line that I am not sure did the contributor do it properly and it might be related (most likely they are able to compile with gnu g++ in wsl/linux without the most correct syntax) which are:

Sudoku.cpp

Sudoku::Sudoku(int boardHeight, int boardWidth) : Board(boardHeight, boardWidth) {}

as well as how he defined and implemented the relationship between the board and sudoku

The error message is at bellow

compile to compile : clang++ -std=c++20 main.cpp

//Error message
Undefined symbols for architecture arm64:
  "Sudoku::initialize(int)", referenced from:
      _main in main-ec9303.o
  "Sudoku::drawBoard()", referenced from:
      _main in main-ec9303.o
  "Sudoku::Sudoku(int, int)", referenced from:
      _main in main-ec9303.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Here is the source code: https://github.com/potatochick2020/Sudoku-core

Minimal code to post here (might not be that minimal):

main.cpp

//main.cpp
#include "Sudoku.h"

int main()
{
    Sudoku sudoku;
    sudoku.initialize();
    sudoku.drawBoard();
}


Sudoku.h

//Sudoku.h
#pragma once
#include <random>
#include <algorithm>
#include <iterator>
#include <iostream>
#include <vector>
#include <set>
#include <unordered_set>
#include <stack>
#include <map>
#include <numeric>

#include "Board.h"

#define SHUFFLE(x) shuffle(x.begin(), x.end(), mt19937{ random_device{}() });
#define CANDIDATE_ARRAY { 1, 2, 3, 4, 5, 6, 7, 8, 0 }

using namespace std;

class Sudoku : public Board
{
private:
    bool GenerateSudoku(void);
public:
    Sudoku(int boardHeight = 9, int boardWidth = 9);
    // default argument only in decleration
    void initialize(int difficulty = 3);
    //overloading for a Board::setBoard 
    void setBoard(int, int, int);
    void setBoard(vector<vector<int>>);
    virtual void drawBoard();
    bool isCorrect(void);
    bool isComplete(void);
    bool isValid(void);
    bool isPlacable(int, int, int);
    int SudokuSolution(void);
};

Board.h

#pragma once
#include <vector>
using namespace std;

// Abstract Class since it has an abstract function
class Board
{
protected:
    vector<vector<int>> board;
    int boardWidth, boardHeight;
    Board(int, int);
    // Abstaract function that must be defined by children
    virtual void drawBoard() = 0;
public:
    vector<vector<int>> getBoard();
    void setBoard(vector<vector<int>> newBoard);
};

Board.cpp

#pragma once
#include <vector>
using namespace std;

// Abstract Class since it has an abstract function
class Board
{
protected:
    vector<vector<int>> board;
    int boardWidth, boardHeight;
    Board(int, int);
    // Abstaract function that must be defined by children
    virtual void drawBoard() = 0;
public:
    vector<vector<int>> getBoard();
    void setBoard(vector<vector<int>> newBoard);
};

Sudoku.cpp

#include "Sudoku.h"

Sudoku::Sudoku(int boardHeight, int boardWidth) : Board(boardHeight, boardWidth) {}

bool Sudoku::isValid()
{
    unordered_set<int> set;
    int tar = 0;
    // check horizontal
    for (int i = 0; i < 9; i++)
    {
        set.clear();
        for (int j = 0; j < 9; j++)
        {
            if (board[i][j] != tar && set.find(board[i][j]) != set.end())
            {
                return false;
            }
            else
            {
                set.insert(board[i][j]);
            }
        }
    }
    // check vertical
    for (int i = 0; i < 9; i++)
    {
        set.clear();
        for (int j = 0; j < 9; j++)
        {
            if (board[j][i] != tar && set.find(board[j][i]) != set.end())
            {
                return false;
            }
            else
            {
                set.insert(board[j][i]);
            }
        }
    }
    // check 3x3 box
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            set.clear();
            for (int k = 0; k < 3; k++)
            {
                for (int l = 0; l < 3; l++)
                {
                    if (board[i * 3 + k][j * 3 + l] != tar && set.find(board[i * 3 + k][j * 3 + l]) != set.end())
                    {
                        return false;
                    }
                    else
                    {
                        set.insert(board[i * 3 + k][j * 3 + l]);
                    }
                }
            }
        }
    }

    return true;
}

bool Sudoku::isComplete()
{
    for (int row = 0; row < 9; row++)
    {
        for (int col = 0; col < 9; col++)
        {
            if (board[row][col] == 0)
                return false;
        }
    }
    return true;
}

bool Sudoku::isCorrect(void)
{
    return isValid() && isComplete();
}

bool Sudoku::isPlacable(int i, int j, int value)
{
    for (int idx = 0; idx < 9; idx++)
    {
        if (board[i][idx] == value)
            return false;
        if (board[idx][j] == value)
            return false;
        if (board[(i / 3) * 3 + (idx / 3)][(j / 3) * 3 + (idx % 3)] == value)
            return false;
    }
    return true;
}

void Sudoku::setBoard(int row, int col, int value)
{
    board[row][col] = value;
}

bool Sudoku::GenerateSudoku()
{
    vector<int> numbers(9);

    while (isComplete() == false)
    {
        for (int row = 0; row < 9; row++)
        {

            for (int col = 0; col < 9; col++)
            {

                if (board[row][col] == 0)
                {
                    // fills out the numbers vector with numbers from 1 -> numbers.size()
                    iota(begin(numbers), end(numbers), 1);
                    SHUFFLE(numbers);
                    for (int number : numbers)
                    {
                        if (isPlacable(row, col, number))
                        {
                            // if (true){
                            board[row][col] = number;
                            if (Sudoku::GenerateSudoku())
                                return true;
                            board[row][col] = 0;
                        }
                    }
                    return false;
                }
            }
        }
    }
    return true;
}

int Sudoku::SudokuSolution()
{
    int ans = 0;
    vector<int> numbers(9);

    // fills out the numbers vector with numbers from 1 -> numbers.size()
    iota(begin(numbers), end(numbers), 1);
    for (int row = 0; row < 9; row++)
    {

        for (int col = 0; col < 9; col++)
        {
            if (board[row][col] == 0)
            {
                for (int number : numbers)
                {
                    if (isPlacable(row, col, number))
                    {
                        board[row][col] = number;

                        if (isCorrect())
                            ans++;
                        else
                            ans += Sudoku::SudokuSolution();

                        board[row][col] = 0;
                    }
                }
                return ans;
            }
        }
    }
    return 0;
}

void Sudoku::initialize(int difficulty)
{
    GenerateSudoku();
    vector<int> rowsCandidates = CANDIDATE_ARRAY;
    SHUFFLE(rowsCandidates);
    vector<int> colsCandidates = CANDIDATE_ARRAY;
    SHUFFLE(colsCandidates);
    pair<pair<int, int>, int> backup = {{rowsCandidates[0], colsCandidates[0]}, board[rowsCandidates[0]][colsCandidates[0]]};
    board[rowsCandidates[0]][colsCandidates[0]] = 0;

    int attempt = 0;
    while (attempt < difficulty)
    {

        rowsCandidates = CANDIDATE_ARRAY;
        SHUFFLE(rowsCandidates);

        colsCandidates = CANDIDATE_ARRAY;
        SHUFFLE(colsCandidates);

        backup = {{rowsCandidates[0], colsCandidates[0]}, board[rowsCandidates[0]][colsCandidates[0]]};
        board[rowsCandidates[0]][colsCandidates[0]] = 0;
        if (SudokuSolution() > 1)
        {
            board[backup.first.first][backup.first.second] = backup.second;
            attempt++;
        }
    }
};

void Sudoku::drawBoard()
{
    // this implementation done by @AshishRaikwar1290
    for (int row = 0; row < boardHeight; row++)
    {
        for (int col = 0; col < boardHeight; col++)
        {
            if (col == 3 || col == 6)
                cout << " | ";
            cout << board[row][col] << " ";
        }
        if (row == 2 || row == 5)
        {
            cout << endl;
            for (int i = 0; i < boardHeight; i++)
                cout << "---";
        }
        cout << endl;
    }
}

I expect it could compile properly, and when i do ./a.out, it should print a sudoku out. (see drawBoard() in Sudoku.cpp)

NOK
  • 17
  • 5
  • How do you *build* your code? Do you remember to build with all source files? Link with all object files? – Some programmer dude Dec 03 '22 at 09:29
  • clang++ -std=c++20 main.cpp This is the only line I do... Should I make a make file instead? – NOK Dec 03 '22 at 09:30
  • You need to list *all* source files when you're building. How would the compiler and linker otherwise know what files you want to build with? – Some programmer dude Dec 03 '22 at 09:31
  • On a totally different note: `using namespace std;` [is a bad practice](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) when done in a source file. Doing it in a header file should be considered plain wrong. – Some programmer dude Dec 03 '22 at 09:32
  • For everyone who encounter this issue: This is because you did not build with all source file as mentioned by @Someprogrammerdude , the solution is `clang++ -std="c++20" -o test Main.cpp Sudoku.cpp Board.cpp ` replace `clang++` with `g++ `if you are using gnu g++. – NOK Dec 04 '22 at 10:23

0 Answers0