Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
195 views
in Technique[技术] by (71.8m points)

c++ - Linking error "LNK2019: unresolved external symbol" without external libraries

I've seem to be having problems with the linker while working on a slightly bigger project. (I'm using Visual Studios 2019. I'm trying to recreate code from Lubos Briedas "Plasma Simulation by Example" and there are some mistakes in the book, even though most of it is fine a great introduction into simulations with C++.)

Currently I receive the following errors:

Output.obj : error LNK2019: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Field_<double> &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$Field_@N@@@Z) referenced in function "void __cdecl Output::fields(class World &,class std::vector<class Species,class std::allocator<class Species> > &)" (?fields@Output@@YAXAAVWorld@@AAV?$vector@VSpecies@@V?$allocator@VSpecies@@@std@@@std@@@Z)
Output.obj : error LNK2019: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Field_<struct vec3<double> > &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$Field_@U?$vec3@N@@@@@Z) referenced in function "void __cdecl Output::fields(class World &,class std::vector<class Species,class std::allocator<class Species> > &)" (?fields@Output@@YAXAAVWorld@@AAV?$vector@VSpecies@@V?$allocator@VSpecies@@@std@@@std@@@Z)
Species.obj : error LNK2019: unresolved external symbol "public: void __thiscall Field_<double>::scatter(struct vec3<double>,double)" (?scatter@?$Field_@N@@QAEXU?$vec3@N@@N@Z) referenced in function "public: void __thiscall Species::computeNumberDensity(void)" (?computeNumberDensity@Species@@QAEXXZ)

I've checked the spelling of the functions mentioned in the messages multiple times and also checked that there is no additional definition. I also looked up whether the operator<<-overloading can be done outside of a class (like in the code) and it seems to be fine. Adding const to the functions in the error messages don't solve them, so I don't think it has something to do with l/rvaulues. Most other solutions to this error I've found included adding something to the linker via the property pages, but since I don't include any special external library, I don't know what I would have to add there and if something needs to be added all.

Are there any other possibilities which might lead to this error? And how can I detect and solve what needs to be added or changed? I've been stuck for quite some time now and hope one of you might be able to help me.

(If needed, I can provide the complete code, but I refrain from it for now, because I don't have a minimal working example and it might be to much without.)

Here is the code for the functions mentioned in the error messages:

// Output.h
#pragma once
#include <sstream>
#include <fstream>
#include <ostream>
#include <iostream>

#include "Fields_.h"
#include "World.h"
#include "Species.h"

namespace Output { void fields(World& world, std::vector<Species> &species); }
           
void Output::fields(World& world, std::vector<Species> &species);
// Output.cpp
#include "Output.h"

// write data to a file stream
template<typename T>
std::ostream& operator<<(std::ostream& out, Field_<T>& f) {
    for (int k = 0; k < f.nk; k++, out << "
") // new line after each "k"
        for (int j = 0; j < f.nj; j++)
            for (int i = 0; i < f.ni; i++)
                out << f.data[i][j][k] << " ";
    return out;
}

// saves output in VTK format
void Output::fields(World& world, std::vector<Species>& species) {
    std::stringstream name;     // build file name
    name << "fields.vti";   // here we just set it to a given string

    // open output file
    std::ofstream out(name.str());
    if(!out.is_open()) { std::cerr << "Coulld not open " << name.str() << std::endl; return; }

    // ImageData is a VTK format for structured Cartesian meshes
    out << "<VTKFile type="ImageData">
";
    double3 x0 = world.getX0();
    double3 dh = world.getDh();
    out << "<ImageData Origin="" << x0[0] << " " << x0[1] << " " << x0[2] << "" ";
    out << "Spacing="" << dh[0] << " " << dh[1] << " " << dh[2] << "" ";
    out << "WholeExtent="0 " << world.ni - 1 << " 0 " << world.nj - 1 << " 0 " << world.nk - 1 << "">
";

    // output data stored on nodes (point data)
    out << "<PointData>
";

    // node volumes, scalar
    out << "<DataArray Name="NodeVol" NumberOfComponents="1" format="ascii" type="Float64">
";
    out << world.node_vol;  // use the overloaded << operator
    out << "</DataArray>
";

    // potential, scalar
    out << "<DataArray Name="phi" NumberOfComponents="1" format="ascii" type="Float64">
";
    out << world.phi;   // use the overloaded << operator
    out << "</DataArray>
";
    /*  */  // output world.phi

    // charge density, scalar
    out << "<DataArray Name="rho" NumberOfComponents="1" format="ascii" type="Float64">
";
    out << world.rho;   // use the overloaded << operator
    out << "</DataArray>
";
    /*  */  // output world.rho

    // electric field, 3 component vector
    out << "<DataArray Name="ef" NumberOfComponents="3" format="ascii" type="Float64">
";
    out << world.ef;    // uses overloaded << from Field_ and vec3
    out << "</DataArray>
";

    // close the tags
    out << "</PointData>
";
    out << "</ImageData>
";
    out << "</VTKFile>
";

    // species number densities
    for (Species& sp : species) {
        out << "<DataArray Name="nd." << sp.name << "" NumberOfComponents="1" format="ascii" type="Float64">
";
        out << sp.den;
        out << "</DataArray>
";
    }
}       // file closed here as 'out'  goes out of scope

Moving the function with the error from the .cpp to the class in .h solved one error. But this isn't possible with the other errors, since there is to class to put them in.

// Fields_.h
#pragma once
#include <ostream>
//#include <utility>
#include "vec3.h"

template <typename T>
class Field_{
public:
    
    // constructor
    Field_(int ni, int nj, int nk) : ni{ ni }, nj{ nj }, nk{ nk }{
        data = new T * *[ni];           // ni pointers to pointers of type T
        for (int i = 0; i < ni; i++) {
            data[i] = new T * [nj];     // allocte nj pointers to T
            for (int j = 0; j < nj; j++)
                data[i][j] = new T[nk]; // allocate nk objects of type T
        }
        // when creating a scalar Field (not Field_<double3>), initialization has to be done explicitly
        if (!std::is_same<T, double3>::value) {
            operator=(0);
        }
        //operator=(0); // call the overloaded operator= function
        //(*this) = 0;                  // clear data (doesn't work)
    }

    // destructor, frees momory in reverse order
    ~Field_() {
        if (data == nullptr) return;        // return if unallocated
        for (int i = 0; i < ni; i++) {      // release memory in reverse order
            for (int j = 0; j < nj; j++)
                delete data[i][j];
            delete data[i];
        }

        delete[] data;
        data = nullptr;                     // mark as free
    }

    // data acces operator
    T** operator[] (int i) { return data[i]; }

    // overload the assignment operator
    Field_<T>& operator= (const T s) {
        for (int i = 0; i < ni; i++)
            for (int j = 0; j < nj; j++)
                for (int k = 0; k < nk; k++)
                    data[i][j][k] = s;
        return *this;                           // return refernce to self
    }

    // copy constructor
    Field_(const Field_& other) :
        Field_{ other.ni,other.nj, other.nk } {
        for (int i = 0; i < ni; i++)
            for (int j = 0; j < nj; j++)
                for (int k = 0; k < nk; k++)
                    data[i][j][k] = other(i, j, k);
        }

    // move construtor
    Field_(Field_ &&other) noexcept:
        ni{ other.ni }, nj{ other.nj }, nk{ other.nk } {
            if (data) this->~Field_();  // deallocate own data /*doesn't work??? why is it needed?*/
            data = other.data;      // steal the data
            other.data = nullptr;   // invalidate
        }

    // move assignment operator
    Field_& operator=(Field_&& f) {
        if (data) ~Field_();    // deallocate own data
        data = f.data; f.data = nullptr; return *this;
    }

    // read-only acces to data[i][j][k]
    T operator() (int i, int j, int k) const { return data[i][j][k]; }

    void operator /=(const Field_& other) {
        for (int i = 0; i < ni; i++)
            for (int j = 0; j < nj; j++)
                for (int k = 0; k < nk; k++) {
                    if (other.data[i][j][k] != 0)
                        data[i][j][k] /= other(i, j, k); // in the book data[i][j][k] /= other[i][j][k];
                    else
                        data[i][j][k] = 0;
                }
    }

    Field_& operator += (const Field_& other) {
        for (int i = 0; i < ni; i++)
            for (int j = 0; j < nj; j++)
                for (int k = 0; k < nk; k++)
                    data[i][j][k] += other(i, j, k);
        return (*this);
    }

    // compound multiplication
    Field_& operator *= (double s) {
        for (int i = 0; i < ni; i++)
            for (int j = 0; j < nj; j++)
                for (int k = 0; k < nk; k++)
                    data[i][j][k] *= s;
        return (*this);
    }

    // multiplikation operator, returns new Field set to f*s
    friend Field_<T> operator*(double s, const Field_<T>& f) {
        Field_<T> r(f);
        return std::move(r *= s);   // force move
        //return move(r *= s);  // force move
        //return r;
        //return r *= s;
    }

    void scatter(double3 lc, double value) {
        // make sure we are in domain
        if (lc[0]<0 || lc[0]>ni - 1 || lc[1]<0 || lc[1]>nj - 1 || lc[2]<0 || lc[2]>nk - 1) return;

        // compute the cell index and the fractional distances
        int i = (int)lc[0];
        double di = lc[0]

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Adding operator<<-overloading (and the scatter function) into the Fields_ class solved these errors. Now there are others issues, but this seems to be fixed.

(@john: It should be possible to define templates out of class as well. But I don't know how in this example and this seems to be sufficient for my needs. If anyone knows a more elegant work around, I'm happy to try it out as well.)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...