0

This is basically two errors in one, that seem to come from the same thing. If I define my functions in my main.cpp file, and forward declare them at the top of the file, it doesn't give me any errors and everything runs smoothly.

When trying to declutter and move things into seperate .cpp files, it starts to give me linker errors and saying things are undefined even though I am using appropriate header files. The code is shown below:

main.cpp

#include "io.h"

#include <iostream>
#include <map>


class exchangeRates;
void menu();

int main()
{
    menu();
    return EXIT_SUCCESS;
}

// class storing exchange rates

class exchangeRates
{
public: // Access specifier

    // Data Members
    std::map<std::string, double> usd = {
                                {"GBP", 1.2},
                                {"EUR", 0.7},
    };
    std::map<std::string, double> gbp = {
                                {"USD", 0.9},
                                {"EUR", 1.4},
    };
};

// menu function

void menu()
{
    // get reference currency code from user
    std::string refCurrency{ obtainCodeFromUser() };

    // create 'rates' instance of 'exchangeRates' class
    exchangeRates rates{};

    // print the exchange values for that currency
    if (refCurrency == "USD")
    {
        printExchangeValues(rates.usd, refCurrency);
    }
    else if (refCurrency == "GBP")
    {
        printExchangeValues(rates.gbp, refCurrency);
    }
    else
    {
        std::cout << "\nInvalid currency code. Example: USD, GBP, EUR etc.\n\n";
        menu();
    }
}

io.h

#ifndef IO_H
#define IO_H

#include <iostream>
#include <map>

std::string obtainCodeFromUser();
double obtainAmountFromUser();
template<typename Map>
void printExchangeValues(Map& valuedCurrencies, std::string refCurrency);

#endif

io.cpp

#include "io.h"

#include <iostream>
#include <map>

// io functions for currency converter

std::string obtainCodeFromUser()
{
    // obatin reference currency code from user
    std::cout << "Enter currency code for reference currency (case-sensitive): ";
    std::cin.clear();
    std::string refCurrency{};
    std::cin >> refCurrency;
    std::cin.ignore(INT_MAX, '\n');

    return refCurrency;
}

double obtainAmountFromUser()
{
    // obtain amount of currency to be converted from user
    std::cout << "Enter amount of currency to convert: ";
    std::cin.clear();
    double amount{};
    std::cin >> amount;
    std::cin.ignore(INT_MAX, '\n');

    return amount;
}

template<typename Map>
void printExchangeValues(Map& valuedCurrencies, std::string refCurrency)
{
    // obtain amount of currency to be converted
    double amount{ obtainAmountFromUser() };
    std::cout << refCurrency << " " << amount << " is worth:\n";
    for (auto& item : valuedCurrencies) {
        std::cout << item.first << ": " << amount * item.second << '\n';
    }
}

I am still a beginner with C++ so some parts have been copied from other open-source programs, and I know there are probably plenty of places to improve my code, but I'm mainly interested in why it just won't compile. The examples I followed when learning how to use header files worked fine and I don't believe I've done anything different here.

It says the identifiers "obtainCodeFromUser" and "printExchangeValues" are undefined. The linker error it gives is LNK2019 'unresolved external symbol ...' and it seems to be relating to the printExchangeValues function.

Any help is massively appreciated!

SwillMith16
  • 49
  • 1
  • 10
  • The template function `printExchangeValues` is trivial to solve. Read this: [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). The seemingly missing `obtainCodeFromUser` on the other hand, makes little sense, assuming you are, in fact, properly bringing all source files into your project correctly. – WhozCraig Mar 20 '22 at 21:55

1 Answers1

0

The issue mentioned by WhozCraig is very useful, I hope you will read it carefully. Regarding your question, after I modified some code, the program can run correctly. The error is caused by the template. Since you are a beginner and the program is not very complicated, the following code is more convenient for you to understand:
main.cpp

#include "io.h"

#include <iostream>
#include <map>

// class storing exchange rates

class exchangeRates
{
public: // Access specifier

    // Data Members
    std::map<std::string, double> usd = {
                                {"GBP", 1.2},
                                {"EUR", 0.7},
    };
    std::map<std::string, double> gbp = {
                                {"USD", 0.9},
                                {"EUR", 1.4},
    };
};
template<typename Map>
void printExchangeValues(Map& valuedCurrencies, std::string refCurrency)
{
    // obtain amount of currency to be converted
    double amount{ obtainAmountFromUser() };
    std::cout << refCurrency << " " << amount << " is worth:\n";
    for (auto& item : valuedCurrencies) {
        std::cout << item.first << ": " << amount * item.second << '\n';
    }
}
// menu function

void menu()
{
    // get reference currency code from user
    std::string refCurrency{ obtainCodeFromUser() };

    // create 'rates' instance of 'exchangeRates' class
    exchangeRates rates{};

    // print the exchange values for that currency
    if (refCurrency == "USD")
    {
        printExchangeValues(rates.usd, refCurrency);
    }
    else if (refCurrency == "GBP")
    {
        printExchangeValues(rates.gbp, refCurrency);
    }
    else
    {
        std::cout << "\nInvalid currency code. Example: USD, GBP, EUR etc.\n\n";
        menu();
    }
}

int main()
{
    menu();
    return EXIT_SUCCESS;
}

io.h

#ifndef IO_H
#define IO_H

#include <iostream>
#include <map>

std::string obtainCodeFromUser();
double obtainAmountFromUser();


#endif

io.cpp

#include "io.h"

#include <iostream>
#include <map>

// io functions for currency converter

std::string obtainCodeFromUser()
{
    // obatin reference currency code from user
    std::cout << "Enter currency code for reference currency (case-sensitive): ";
    std::cin.clear();
    std::string refCurrency{};
    std::cin >> refCurrency;
    std::cin.ignore(INT_MAX, '\n');

    return refCurrency;
}

double obtainAmountFromUser()
{
    // obtain amount of currency to be converted from user
    std::cout << "Enter amount of currency to convert: ";
    std::cin.clear();
    double amount{};
    std::cin >> amount;
    std::cin.ignore(INT_MAX, '\n');

    return amount;
}
Yujian Yao - MSFT
  • 945
  • 1
  • 3
  • 9
  • Thanks! that really helped. Seems the only change was moving the printExchangeValues back into main.cpp right? I'll take a look at templates in header files as you and WhozCraig have suggested. Cheers! – SwillMith16 Mar 21 '22 at 12:57