0

I'm working on a MP3 player for an assignment. I keep getting the following errors:

1>A4_main.obj : error LNK2019: unresolved external symbol "public: static void __cdecl Song::ClearListFile(void)" (?ClearListFile@Song@@SAXXZ) referenced in function "void __cdecl DeleteSong(void)" (?DeleteSong@@YAXXZ)
1>A4_main.obj : error LNK2001: unresolved external symbol "public: static char * Song::songListFile_" (?songListFile_@Song@@2PADA)
1>A4_Song.obj : error LNK2001: unresolved external symbol "public: static char * Song::songListFile_" (?songListFile_@Song@@2PADA)
1>A4_Song.obj : error LNK2019: unresolved external symbol __imp__mciSendStringA@16 referenced in function "public: void __thiscall Song::PlaySong(void)" (?PlaySong@Song@@QAEXXZ)
1>A4_Song.obj : error LNK2001: unresolved external symbol "public: static char * Song::currentMP3_" (?currentMP3_@Song@@2PADA)

From what I understand, these sort of errors stem from not including function declarations, declaring but not implementing them, misspelling, etc. What have I missed here? Since this is an assignment, I'll post the bare minimum of the code I think is causing the problem.

A4_main.cpp

#include "A4_LinkedList.h"
#include "A4_Song.h"

#include <iostream>
#include <fstream>
#include <cstdio>
#include <string>

using namespace std;

LinkedList g_list;

void FreeLinkedList();
char DisplayMenu();
void LoadSongFile();
void AddNewSong();
void DeleteSong();
void PlaySong();
void PrintAllSongs();

//stuff

void LoadSongFile()
{
    const int SZ = 256;
    int songCnt = 0;
    ifstream inData;
    char buff[SZ];
    Song* newSong;

    _flushall();
    cout << "\nEnter the full file path: ";
    cin.getline(Song::songListFile_, SZ);

    // Open the file
    inData.open(Song::songListFile_);

    // Free any memory currently allocated for the word array
    FreeLinkedList();

    // Loop through file again and allocate memory
    while (!inData.eof())
    {
        // Each time through loop read all 5 entries in each line.
        // Songt with the Song name
        inData.getline(buff, SZ);

        if (buff[0] == 0)
        {
            // No more words
            break;
        }

        // Create a new Song object
        newSong = new Song(buff);

        if (newSong == 0)
        {
            cout << "\nDynamic memory allocation failed.";
            break;
        }

        // Add this Song object to the linked list
        g_list.AddLinkToBack(newSong);

        songCnt++;
    }

    inData.close();

    cout << "\nLoaded file and read " << songCnt << " Song objects.";
}

void DeleteSong()
{
    const int SZ    = 256;
    bool foundSong  = false;
    Node* node = g_list.GetFirstNode();
    Song* song = 0;
    char songFileName[SZ];

    _flushall();

    // Prompt the user for the name of a song
    cout << "\nEnter the song name with extension: ";
    cin.getline(songFileName, SZ);

    // Loop through the linked list for that song and delete it if it is found.
    // If not, print error to console.
    while (node != 0)
    {
        // Cast the void ptr to a song object ptr
        song = (Song*)(node->data_);

        // Call on the Song class to print the objects contents
        if (strcmp(song->GetSongName(), songFileName) == 0)
        {
            // Set flag and get out of loop
            g_list.RemoveThisLink(node);
            foundSong = true;
            break;
        }

        // Go to the next node
        node = node->next_;
    }

    if (!foundSong)
    {
        cout << "\nCould not find that song in list!\n";
    }
    else
    {
        // Now that the linked list has been updated need to persist the new
        // list to the song file, replacing previous contents.
        Song::ClearListFile();

        // Now loop through the linked list again, appending the song
        // file name to the song list file.
        node = g_list.GetFirstNode();

        while (node != 0)
        {
           // Cast the void ptr to a song object ptr then add name to file
            song = (Song*)(node->data_);
            song->AppendToListFile();

            // Go to the next node
            node = node->next_;
        }
    }
}

A4_Song.cpp

#include "A4_Song.h"

#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <Windows.h>

using namespace std;

// Store the path name of the song list file
static char songListFile_[ENTRY_SZ] = "";
static char currentMP3_[ENTRY_SZ] = "";

// Static method to empty the song list file
static void ClearListFile()
{
    ofstream outFile;

    if (songListFile_[0] != 0)
    {
        // Open for truncate then close again
        outFile.open(songListFile_, ios_base::ate);
        outFile.close();
    }
    else
        cout << "\nNothing to clear!";
}

void Song::PlaySong()
{
    const int BUFF_SZ = 512;
    char fullStr[BUFF_SZ];
    MCIERROR err;

    StopSong();

    // Set global variable so we know this file is playing.
    // Sandwich the file name in escaped double quotes so
    // spaces can be included and don't need to double up
    // on the backslashes.
    sprintf_s(currentMP3_, ENTRY_SZ, "\"%s\"", songPath_);
    sprintf_s(fullStr, BUFF_SZ, "open %s type mpegvideo alias myFile", currentMP3_);
    err = mciSendString(fullStr, NULL, 0, 0); 
    err = mciSendString("play myFile", NULL, 0, 0);
}

Let me know if I've omitted too much.

  • Given you have posted minimal code, it's impossible to tell for sure, but from what I looked at, You have not declared ClearListFile() in the header file, and thus, the linker doesn't know where to look for it. I would suggest that you post, at the least, the header file for the song class, so we can see which functions you have declared. – Ian Young Nov 10 '15 at 04:12
  • Is `ClearListFile` supposed to be a part of class `Song` as a static member function, or is it supposed to be a namespace level function with internal linkage? – Weak to Enuma Elish Nov 10 '15 at 04:12
  • 1
    Regarding the mciSendString() call, If you are using visual studio, I would make sure that winmm.lib is added under "Additional Dependencies", in project settings. That is, if you haven't used the Windows SDK integration tool with Visual Studio – Ian Young Nov 10 '15 at 04:22

1 Answers1

0

Your problem is that you have variables and functions declared inside Song, but are defined at namespace level. This makes them different entities, and so definitions for the declarations in Song are never found.

static char songListFile_[ENTRY_SZ] = "";
static char currentMP3_[ENTRY_SZ] = "";
static void ClearListFile() {/*...*/}

These should be changed. Removing static and prefixing the surrounding class Song should fix it.

char Song::songListFile_[ENTRY_SZ] = "";
char Song::currentMP3_[ENTRY_SZ] = "";
void Song::ClearListFile() {/*...*/}

You only need the static inside the class definition. Outside it, you're using a different meaning of the static keyword. I think you have more instances like this outside the code you posted, but it shouldn't be difficult to find them.

Community
  • 1
  • 1
Weak to Enuma Elish
  • 4,622
  • 3
  • 24
  • 36
  • D'oh! Well, that fixed songListFile_, currentMP3_, and ClearListFile. I'm still getting the following: `error LNK2019: unresolved external symbol __imp__mciSendStringA@16 referenced in function "public: void __thiscall Song::PlaySong(void)" (?PlaySong@Song@@QAEXXZ)` – SorrieCharly Nov 10 '15 at 04:26
  • @SorrieCharly You should read Ian Young's comment on the question. That one must be an external dependency that you need to make the linker aware of. – Weak to Enuma Elish Nov 10 '15 at 04:28
  • I see it now! Thank you. – SorrieCharly Nov 10 '15 at 04:31