0

我在为包含 3 个 c++ 文件的项目创建 makefile 时遇到问题。

在包含文件(tree.h、helperMethods.h 和 main.cpp)的目录中,我首先使用命令emacs makefile.make。然后,在 emacs 中,我将 makefile 编写为:

all: tree.h helperMethods.h main.cpp
[tab]g++ -std=c++0x -o project3 tree.h helperMethods.h main.cpp

我已经手动验证了命令g++ -std=c++0x -o project3 tree.h helperMethods.h main.cpp确实编译了这三个文件并生成了一个可以运行的可执行文件。

在此之后,我保存 makefile,退出 emacs,并尝试运行它。

首先我使用了 make但这返回了:

make: *** No targets specified and no makefile found.  Stop.

然后我尝试使用make -f makefile.make但这也不起作用并返回:

make: Nothing to be done for `all'.

在这一点上,我不确定为什么生成文件没有正确构建。我对makefile没有太多经验;我很确定我在 makefile 正文中的命令是正确的,但是我在编写它时没有正确设置它。

如果它完全相关,这里有 3 个文件:

树.h:

#ifndef tree_h
#define tree_h
#include <vector>

using namespace std;

template<class T>
class tree
{
public:
    tree<T> * parent;
    T element;
    vector<tree<T> > nodes;
    tree(const T& theElement)
    {
        element = theElement;
        nodes = vector<tree<T> >();
    }
    tree()
    {
        nodes = vector<tree<T> >();
    }
};

#endif

助手方法.h

#ifndef helperMethods_h
#define helperMethods_h

#include "tree.h"

#include <tuple>
#include <string>
#include <iostream>
#include <vector>

using namespace std;

string printName(tuple<string, string, string> myTuple){
    string ret = "";
    if(std::get<0>(myTuple).length() > 0)
        ret += get<0>(myTuple) + " ";
    if(std::get<1>(myTuple).length() > 0 || std::get<2>(myTuple).length() > 0)
        ret += "(";
    if(std::get<1>(myTuple).length() > 0)
        ret += get<1>(myTuple);
    if(std::get<1>(myTuple).length() > 0 && std::get<2>(myTuple).length() > 0)
        ret += ", ";
    if(std::get<2>(myTuple).length() > 0)
        ret += get<2>(myTuple);
    if(std::get<1>(myTuple).length() > 0 || std::get<2>(myTuple).length() > 0)
        ret += ")";
    return ret;
}

bool tupleContain(tuple<string, string, string> myTuple, string myString){
    return (std::get<0>(myTuple).compare(myString) == 0) || (std::get<1>(myTuple).compare(myString) == 0);
}

void findElement(tree<tuple<string, string, string> > myTree, string myString, bool* found, vector<int> *ret)
{
    if(tupleContain(myTree.element, myString))
        *found = true;
    if(! * found)
    {
        for(int counter = 0; counter < (int)myTree.nodes.size(); counter ++)
        {
            if(!* found)
            {
                (*ret).push_back(counter);
                findElement(myTree.nodes.at(counter), myString, found, ret);
                if(!* found)
                    (*ret).pop_back();
            }
        }
    }
}

void getLineage(tree<tuple<string, string, string> > myTree, string myString){
    bool dummyForFound = false;
    bool * found = & dummyForFound;
    vector<int> lineage = vector<int>();
    vector<int> * pointer = & lineage;
    findElement(myTree, myString, found, &lineage);
    if(lineage.size() == 0)
    {
        cout << "Species not present" << endl;
        return;
    }
    vector<string> printString = vector<string>(lineage.size() + 1);
    tree<tuple<string, string, string> > * currentNodePointer = & myTree;
    for(int counter = 0; counter <= (int) lineage.size(); counter ++)
    {
        string currentLine = "";
        for(int counter2 = 0; counter2 < 2*((int) lineage.size() - counter); counter2 ++)
            currentLine += ">";
        if(counter != lineage.size())
            currentLine += " ";
        tree<tuple<string, string, string> > currentNode = * currentNodePointer;
        currentLine += printName(currentNode.element);
        if(counter < (int) lineage.size())
        {
            int foo = lineage.at(counter);
            tree<tuple<string, string, string> > currentNodeDummy = currentNode.nodes.at(foo);
            *currentNodePointer = currentNodeDummy;
        }
        printString.at(counter) = currentLine;
    }
    for(int counter = 0; counter < (int) printString.size(); counter ++)
        cout << printString.at(printString.size() - counter - 1) << endl;
    cout << endl;
}

void getCommonLineage(tree<tuple<string, string, string> > myTree , string name1, string name2)
{
    bool dummyForFound = false;
    bool * found = & dummyForFound;
    vector<int> lineage1 = vector<int>();
    vector<int> * pointer1 = & lineage1;
    vector<int> lineage2 = vector<int>();
    vector<int> * pointer2 = & lineage2;
    findElement(myTree, name1, found, pointer1);
    * found = false;
    findElement(myTree, name2, found, pointer2);
    if(lineage2.size() == 0 || lineage1.size() == 0)
    {
        cout << "At least one species not present." << endl;
        return;
    }
    bool stillSame = lineage1.at(0) == lineage2.at(0);
    cout << "Level[0] Common Ancestor: ROOT (ROOT, ROOT)" << endl;
    tree<tuple<string, string, string>> * lastSharedNode = & myTree;
    int finalCounter = 0;
    for(int counter = 0; counter < (int) min(lineage1.size(), lineage2.size()) && stillSame; counter ++)
    {
        tree<tuple<string, string, string> > dummyNode = * lastSharedNode;
        tree<tuple<string, string, string> > currentNode = dummyNode.nodes.at(lineage1.at(counter));
        *lastSharedNode = currentNode;
        if(counter < (int) min(lineage1.size(), lineage2.size()) - 1 && lineage1.at(counter + 1) != lineage2.at(counter + 1))
            stillSame = false;
        tuple<string, string, string> currentElement = currentNode.element;
        cout << "Level[" << counter + 1 << "] Commont Ancestor: " << printName(currentElement) << endl;
        finalCounter ++;
    }
    cout << endl;
    cout << "Ancestry unique to " << name1 << endl;
    tree<tuple<string, string, string> > savedNode = *lastSharedNode;
    tree<tuple<string, string, string> > * currentUnsharedNode = lastSharedNode;
    for(int counter = finalCounter; counter < (int) lineage1.size(); counter ++)
    {
        tree<tuple<string, string, string> > dummyNode = * currentUnsharedNode;
        tree<tuple<string, string, string> > currentNode = dummyNode.nodes.at(lineage1.at(counter));
        tuple<string, string, string> currentElement = currentNode.element;
        *currentUnsharedNode = currentNode;
        cout << "Level[" << counter + 1 << "] ";
        if(counter == lineage1.size() - 1)
            cout << "Species of interest: ";
        cout << printName(currentElement) << endl;
    }
    cout << endl;
    currentUnsharedNode = &savedNode;
    cout << "Ancestry unique to " << name2 << endl;
    for(int counter = finalCounter; counter < (int) lineage2.size(); counter ++)
    {
        tree<tuple<string, string, string> > dummyNode = * currentUnsharedNode;
        tree<tuple<string, string, string> > currentNode = dummyNode.nodes.at(lineage2.at(counter));
        tuple<string, string, string> currentElement = currentNode.element;
        *currentUnsharedNode = currentNode;
        cout << "Level[" << counter + 1 << "] ";
        if(counter == lineage2.size() - 1)
            cout << "Species of interest: ";
        cout << printName(currentElement) << endl;
    }
    cout << endl;
}
#endif

主文件

#include "rapidxml.h"
#include "tree.h"
#include "helperMethods.h"

#include <string>
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <vector>
#include <queue>
#include <tuple>

using namespace rapidxml;

int main(int argc, const char * arv[]){
    tuple<string, string, string> human ("Human", "Homo sapiens", "Species");
    tuple<string, string, string> apes ("Apes", "", "");
    tuple<string, string, string> dogs ("Dogs", "", "");
    tuple<string, string, string> root ("Root", "", "");
    tuple<string, string, string> bears ("Bears", "", "");
    tuple<string, string, string> cat ("Cat", "", "");
    tuple<string, string, string> horse ("Horse", "", "");
    tree<tuple<string, string, string> > myTree = tree<tuple<string, string, string>>(root);
    tree<tuple<string, string, string> > b = tree<tuple<string, string, string>>(dogs);
    tree<tuple<string, string, string> > c = tree<tuple<string, string, string>>(apes);
    tree<tuple<string, string, string> > d = tree<tuple<string, string, string>>(bears);
    tree<tuple<string, string, string> > e = tree<tuple<string, string, string>>(horse);
    tree<tuple<string, string, string> > f = tree<tuple<string, string, string>>(human);
    tree<tuple<string, string, string> > h = tree<tuple<string, string, string>>(cat);
    d.nodes.push_back(f);
    e.nodes.push_back(h);
    b.nodes.push_back(d);
    b.nodes.push_back(e);
    myTree.nodes.push_back(b);
    myTree.nodes.push_back(c);
    cout << printName(myTree.nodes.at(0).element);

    cout << "Welcome to my Tree of Life program!" << endl << endl;

    int choice = 1;
    while(choice == 1 || choice == 2) {
        cout << "Please choose from the following options:" << endl;
        cout << "   1.Get the lineage of a species" << endl;
        cout << "   2.Get the commmon lineage of two species" << endl;
        cout << "   3.Exit program" << endl << endl;
        cin >> choice;
        cout << endl;
        if(choice == 1)
        {
            cout << "Please enter the name of the species of interest:" << endl;
            string name;
            cin >> name;
            cout << endl;
            getLineage(myTree, name);
        }
        if(choice == 2)
        {
            string name1, name2;
            cout << "Please enter the name of the first species: " << endl;
            cin >> name1;
            cout << "Please enter the name of the second species: " << endl;
            cin >> name2;
            getCommonLineage(myTree, name1, name2);
        }
    }
    return 0;
}
4

2 回答 2

3

Makefile 通常在没有任何扩展名的情况下被调用Makefile(尽管也可以)。makefile如果您使用扩展名(不推荐),则必须告知makemakefile 的名称。这是乏味和不必要的。

其次,您不需要将头文件放在编译命令行中,也不应该这样做。您使用#include文件中的行来指定头文件。所以你的编译命令可能如下所示:

g++ -std=c++0x -o project3 main.cpp

现在,在该命令中:

  • main.cpp

  • project3目标(即将创建的文件。)

还:

  • tree.h并且helperMethods.h是编译工作所必需的;编译取决于此文件。(您知道,但它们不会出现在命令行中,因此并不明显。)此外,如果它们发生变化,您必须重新编译您的文件。

一个 makefile 解释了如何从它的源中创建一个目标,并且还列出了目标依赖关系。(从技术上讲,make 不区分依赖项;它认为它们都是先决条件。但是很容易识别差异。)

因此对于上述命令,整个 make 配方可能如下所示:

project3: main.cpp tree.h helperMethods.h 
        g++ -std=c++0x -o project3 main.cpp

通常,我们不使用像main.cpp;这样的文件名。相反,我们会调用 project3 的主文件project3.cpp,就像目标(输出)文件是project3. 事实上,如果你这样做了,并且你没有任何其他依赖项(头文件),你可以输入:

make project3

根本没有makefile,并且make会想出命令:

g++ -o project3 project3.cpp

在这种情况下,该命令将是错误的,因为它没有指定正确的 C++ 标准。但它通常有效,因此使用相同的基本名称命名您的目标和源是一个很好的理由。

还有一件事:你的文件tree.h是一个只有头文件的库,这很好。但helperMethods.h只是伪装成一个头文件。这确实是一个完整的实现。你应该解决这个问题。

Also, it's really a bad idea to put using namespace std in a header file. Header files should explicitly use the std:: namespace prefix on everything which needs it. It's generally not recommended to use using namespace std anywhere, but it's particularly bad in header files because it silently pollutes the default namespace of any file which includes the header. This can lead to very obscure bugs, or compilation errors.

于 2013-03-17T06:42:52.077 回答
1

学校项目,嗯? project3对于您自己正在做的事情来说,这似乎是一个可怕的任意名称。

您不需要在 makefile 处理中包含 .h 文件,因为严格来说,它们永远不需要独立编译。您的 makefile 将需要一个从 main.cpp 构建 main.o 的规则,然后是一个从 main.o 构建可执行文件的规则。头文件应该作为 main.o 的依赖项添加,这样如果它们中的任何一个发生变化,make就会知道从那里重新构建。

基本上,它需要看起来像这样:

# http://stackoverflow.com/questions/15458126/unsure-how-to-create-a-makefile
all: project3

clean:
    -rm main.o project3

project3: main.o
    g++ -std=c++0x -o project3 main.o

main.o: main.cpp tree.h helperMethods.h
    g++ -std=c++0x -c main.cpp

其他人可能会为此目的生成一个截然不同但同样有效的 makefile。这个过程有一定程度的自由度。

您在这里拥有的是构建整个事物的规则(请记住,如果没有提供目标,则会自动选择 makefile 中的第一个目标),清理任何中间文件的规则,从构建可执行文件的规则目标文件,以及从源文件构建目标文件的规则。这种方法不能很好地扩展。手动管理更大、更复杂的 makefile 的所有依赖项是不愉快的,但在这个非常简单的情况下,这很好。

于 2013-03-17T06:40:29.627 回答