0

I always run into troubles, when I am trying to work with more than two C/C++ files.

For example: Main.cpp

// 
// Created by Niclas Schwalbe on 02.03.21.
//

#include "bTree.h"

int main(){
    BTree<int> * tree = new BTree<int>(2048, [](int a, int b) {return a < b;}, [](int a){printf("%d", a);});
    srand (0);

    for(int i = 0; i < 100000; i++){
        tree->insert(rand());
    }

    tree->print();
}

BTree.cpp

/* B-Tree
 * Author:  Caleb Baker
 * Date:    10/8/17
 * Summary: A B-Tree data structure. Supports lg(n) time search, insert, and delete.
 */

#include "bTree.h"


// Constructor for b tree.
// t is the minimum degree of the tree.
// compare is the comparison function used for managing elements within the tree.
// printK is a function that prints keys.
template <typename T>
BTree<T>::BTree(unsigned t, std::function<bool(T,T)> compare, std::function<void(T)> printK) {
    //Code
}


// Destructor.
template <typename T>
BTree<T>::~BTree<T>() {
    //Code
}


// Inserts the key k into the tree.
template <typename T>
void BTree<T>::insert(T k) {

    // Code
}


// Removes k from the tree. Returns the removed key.
// Throws a BTREE_EXCEPTION if key is not found.
template <typename T>
T BTree<T>::remove(T k) {
    //Code
}


// Function to find a key in the tree.
// returnValue.first is the node the item is in.
// returnValue.second is the correct index in that node's key array
template <typename T>
std::pair<BNode<T>*, unsigned> BTree<T>::search(T k) {

    //Code
}


// Function to find a key in the tree.
// Returns the key.
// If the item was not found an exception is thrown.
template <typename T>
T BTree<T>::searchKey(T k) {
    //Code
}


// Function for printing a tree.
template <typename T>
void BTree<T>::print() {
    //Code
}


// Initialize a b tree node.
// x is a pointer to the node
// t is the minimum degree of the tree.
template <typename T>
void BTree<T>::initializeNode(BNode<T> *x) {
    //Code
}


// Recursively deletes the subtree rooted at x.
// Does the dirty work for the destructor.
template <typename T>
void BTree<T>::freeNode(BNode<T> *x) {
    //Code
}


// Finds the index of k in x->key.
// If k is not present, returns the index of the subtree
// that could contain k in x->child.
template <typename T>
unsigned BTree<T>::findIndex(BNode<T> *x, T k) {
    //Code
}


// Inserts k into x.
// Returns the index of k in x->key.
template <typename T>
unsigned BTree<T>::nodeInsert(BNode<T> *x, T k) {
    //Code
}


// Deletes the indexth element from x->key.
// Returns deleted key.
template <typename T>
T BTree<T>::nodeDelete(BNode<T> *x, unsigned index) {
//Code
}


// Function for splitting nodes that are too full.
// x points to the parent of the node to splits.
// i is the index in x's child array of the node to split.
template <typename T>
void BTree<T>::splitChild(BNode<T> *x, int i) {
      //Code
}


// Merges the (i + 1)th child of parent with the ith child of parent.
// Returns an indicator of whether the change affected the root.
template <typename T>
char BTree<T>::mergeChildren(BNode<T> *parent, unsigned i) {

    //Code 
}


// Makes sure parent->child[index] has at least minDegree items.
// If it doesn't, then things are changed to make sure it does.
// Returns a code indicating what action was taken.
template <typename T>
char BTree<T>::fixChildSize(BNode<T> *parent, unsigned index) {
    //Code
}


// Recursize function for printing a tree or subtree.
// node is the root of the subtree to be printed.
// tab is how far to indent the subtree.
template <typename T>
void BTree<T>::printNode(BNode<T> *node, unsigned tab) {
//Code
}

BTree.h

/* B-Tree
 * Author:  Caleb Baker
 * Date:    10/8/17
 * Summary: A B-Tree data structure.
 *          Most standard operations run in O(lg(n)) time.
 *          Uses O(n) memory.
 *          Where n is the number of items in the tree.
 */


#ifndef BTREE_H
#define BTREE_H

#include <utility>
#include <stdlib.h>
#include <stdio.h>
#include <functional>

#define SEARCH_KEY_NOT_FOUND 's'
#define REMOVE_KEY_NOT_FOUND 'r'

#define NEW_ROOT 2
#define MODIFIED_NOT_ROOT 1
#define NOT_MODIFIED 0


// struct for representing nodes of a b tree
template <typename T>
struct BNode {
    BNode<T> **child;   // Array of pointers to children.
    T *key;             // Array of keys.
    unsigned size;      // Number of keys.
    bool leaf;          // Whether the node is a leaf.
};


typedef char BTREE_EXCEPTION;


// class for representing b trees.
template <typename T>
class BTree {
public:
    // Constructor
    // First parameter is the minimum degree of the tree.
    // Second parameter is the tree's key-comparison function.
    // Third parameter is a function that prints keys.
    // Constant time.
    BTree(unsigned, std::function<bool(T,T)>, std::function<void(T)>);

    // Destructor.
    // Linear time.
    ~BTree<T>();

    // Inserts a key into the tree.
    // Logorithmic time.
    void insert(T);

    // Removes a key from the tree.
    // Throws a BTREE_EXCEPTION if no item was found to remove.
    // Logorithmic time.
    T remove(T);

    // Function to find a key in the tree.
    // returnValue.first is the node the item is in.
    // returnValue.second is the correct index in that node's key array
    // Logorithmic time.
    std::pair<BNode<T>*, unsigned> search(T);

    // Uses search but just returns the key rather than the whole node.
    // Useful when T is a key value pair and lessThan only looks at the key.
    // Throws a BTREE_EXCEPTION if no item matching the parameter is found
    // Logorithmic time.
    T searchKey(T);

    // Prints the tree.
    // Linear time
    void print();

private:

    // Used for initializing nodes.
    void initializeNode(BNode<T>*);

    // Recursive function called by destructor.
    void freeNode(BNode<T>*);

    // Finds the index of a key in a node.
    unsigned findIndex(BNode<T>*, T);

    // Inserts a key into a node.
    unsigned nodeInsert(BNode<T>*, T);

    // Deletes the key at a given index from a node.
    T nodeDelete(BNode<T>*, unsigned);

    // Function for splitting nodes that are too full.
    void splitChild(BNode<T>*, int);

    // Merges two children of a node at a given index into one child.
    char mergeChildren(BNode<T>*, unsigned);

    // Makes sure the child of a node at a specified index has >= minDegree items.
    char fixChildSize(BNode<T>*, unsigned);

    // Recursively prints a subtree.
    void printNode(BNode<T>*, unsigned);

    // Root node.
    BNode<T> *root;

    // Comparison function used for managing element placement.
    std::function<bool(T,T)> lessThan;

    // Function used to print items in the tree.
    std::function<void(T)> printKey;

    // Minimum degree of the tree.
    unsigned minDegree;
};

#endif

Compiling this with:

 gcc -std=c++11 -x c++ Main.cpp bTree.cpp

Will run into inexplainable linking errors, namley:

Undefined symbols for architecture x86_64:...

I already used guards and so on, so what am I doing wrong? Is there any approach, which can ensure that linking error cannot happen in general?

EDIT: Full error message:

  "BTree<int>::print()", referenced from:
      _main in Main-ad069e.o
  "BTree<int>::insert(int)", referenced from:
      _main in Main-ad069e.o
  "BTree<int>::BTree(unsigned int, std::__1::function<bool (int, int)>, std::__1::function<void (int)>)", referenced from:
      _main in Main-ad069e.o
  "std::logic_error::logic_error(char const*)", referenced from:
      std::length_error::length_error(char const*) in Main-ad069e.o
  "std::length_error::~length_error()", referenced from:
      std::__1::__throw_length_error(char const*) in Main-ad069e.o
  "std::terminate()", referenced from:
      ___clang_call_terminate in Main-ad069e.o
  "typeinfo for std::length_error", referenced from:
      std::__1::__throw_length_error(char const*) in Main-ad069e.o
  "vtable for __cxxabiv1::__class_type_info", referenced from:
      typeinfo for std::__1::__function::__base<bool (int, int)> in Main-ad069e.o
      typeinfo for main::$_0 in Main-ad069e.o
      typeinfo for std::__1::__function::__base<void (int)> in Main-ad069e.o
      typeinfo for main::$_1 in Main-ad069e.o
  NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
  "vtable for __cxxabiv1::__si_class_type_info", referenced from:
      typeinfo for std::__1::__function::__func<main::$_0, std::__1::allocator<main::$_0>, bool (int, int)> in Main-ad069e.o
      typeinfo for std::__1::__function::__func<main::$_1, std::__1::allocator<main::$_1>, void (int)> in Main-ad069e.o
  NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
  "vtable for std::length_error", referenced from:
      std::length_error::length_error(char const*) in Main-ad069e.o
  NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
  "operator delete(void*)", referenced from:
      _main in Main-ad069e.o
      std::__1::__function::__func<main::$_0, std::__1::allocator<main::$_0>, bool (int, int)>::~__func() in Main-ad069e.o
      std::__1::_DeallocateCaller::__do_call(void*) in Main-ad069e.o
      std::__1::__function::__func<main::$_1, std::__1::allocator<main::$_1>, void (int)>::~__func() in Main-ad069e.o
  "operator new(unsigned long)", referenced from:
      _main in Main-ad069e.o
      std::__1::__libcpp_allocate(unsigned long, unsigned long) in Main-ad069e.o
  "___cxa_allocate_exception", referenced from:
      std::__1::__throw_length_error(char const*) in Main-ad069e.o
  "___cxa_begin_catch", referenced from:
      ___clang_call_terminate in Main-ad069e.o
  "___cxa_free_exception", referenced from:
      std::__1::__throw_length_error(char const*) in Main-ad069e.o
  "___cxa_pure_virtual", referenced from:
      vtable for std::__1::__function::__base<bool (int, int)> in Main-ad069e.o
      vtable for std::__1::__function::__base<void (int)> in Main-ad069e.o
  "___cxa_throw", referenced from:
      std::__1::__throw_length_error(char const*) in Main-ad069e.o
  "___gxx_personality_v0", referenced from:
      _main in Main-ad069e.o
      std::__1::__function::__func<main::$_0, std::__1::allocator<main::$_0>, bool (int, int)>::__func(main::$_0&&, std::__1::allocator<main::$_0>&&) in Main-ad069e.o
      std::__1::__function::__func<main::$_0, std::__1::allocator<main::$_0>, bool (int, int)>::__clone() const in Main-ad069e.o
      std::__1::__function::__func<main::$_0, std::__1::allocator<main::$_0>, bool (int, int)>::destroy_deallocate() in Main-ad069e.o
      std::__1::__function::__func<main::$_0, std::__1::allocator<main::$_0>, bool (int, int)>::target(std::type_info const&) const in Main-ad069e.o
      std::__1::__throw_length_error(char const*) in Main-ad069e.o
      std::__1::unique_ptr<std::__1::__function::__func<main::$_0, std::__1::allocator<main::$_0>, bool (int, int)>, std::__1::__allocator_destructor<std::__1::allocator<std::__1::__function::__func<main::$_0, std::__1::allocator<main::$_0>, bool (int, int)> > > >::unique_ptr<true, void>(std::__1::__function::__func<main::$_0, std::__1::allocator<main::$_0>, bool (int, int)>*, std::__1::__dependent_type<std::__1::__unique_ptr_deleter_sfinae<std::__1::__allocator_destructor<std::__1::allocator<std::__1::__function::__func<main::$_0, std::__1::allocator<main::$_0>, bool (int, int)> > > >, true>::__good_rval_ref_type) in Main-ad069e.o
      ...
ld: symbol(s) not found for architecture x86_64

Niclas
  • 127
  • 11

0 Answers0