Stroustrup的Simple_window.h

8

我正在尝试从Stroustrup的《C++ Primer》一书中学习图形实例,但目前尚未成功。我已经安装了fltk相关组件,并且通过附录中提供的一个程序成功地显示了一个窗口。

#include <FL/Fl.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Window.H>

int main(){

    Fl_Window window(200,200, "title here");
    Fl_Box box(0,0,200,200,"Hey, hello wrld");
    window.show();
    return Fl::run();
}

然而,尝试使用他的Simple_window.h(可以在他的网站上找到)创建自己的程序时,会出现“对‘Window’的引用不明确”的错误,因为它已经在usr/include/X11/X.h中定义。因此,我尝试将命名空间指定为相关的命名空间:

struct Simple_window : Graph_lib::Window {  //Changed Window to inc. namespace
    Simple_window(Point xy, int w, int h, const string& title );

    bool wait_for_button(); // simple event loop

.
.
.

但是这给我带来了更多我不理解的错误:
$ clear; g++ -Wno-deprecated window.cpp -o holz
    /tmp/ccIFivNg.o: In function `main':
    window.cpp:(.text+0x64): undefined reference to `Simple_window::Simple_window(Point, int, int, String const&)'
    /tmp/ccIFivNg.o: In function `Graph_lib::Window::~Window()':
    window.cpp:(.text._ZN9Graph_lib6WindowD2Ev[_ZN9Graph_lib6WindowD5Ev]+0x14): undefined reference to `vtable for Graph_lib::Window'

我觉得掌握图形技术将是一条漫长而曲折的道路 -_-

看起来你从未定义过 Simple_window::Simple_window(Point, int, int, String const&),你有在任何地方定义过吗? - Mooing Duck
1
你使用了合格的名称是正确的。这使得你的代码有所进展。现在你的代码可以编译,但是你的项目无法链接。看起来你没有为Simple_window的构造函数提供定义,并且你在Graph_lib::Window中有虚函数,但没有定义虚析构函数。也许你只是没有将所有正确的.o文件链接在一起。这些事情与图形无关。 - Lightness Races in Orbit
你的“取得进展”让我感到异常高兴,不知为何。问题已解决,窗口终于可以正常工作了(历经三天的折磨)。现在只需等待6个小时回答自己的问题,这样其他人也能找到答案解决类似的问题。 - Nathan Hutton
6个回答

5
任何处于相同困境的人,我在这里留下了我所做的事情,最终能够编译并获取Stroustrup的书“Programming: Principles and Practice using C++, 2nd Edition”的第12.3节中FLTK的第一个程序窗口。

在Kubuntu 14.04上安装FLTK后:

$ sudo apt install libfltk1.3-dev

我可以使用附录D中的示例程序进行编译。
$ fltk-config --compile fltkTest.cpp

感谢这篇文章,我终于明白如何跟着第12章的第一个例子走了。通过比较cwivagg和Nathan的命令与fltk-config生成的命令,我最终得到了这个命令。

$ clang++ -I/usr/include/cairo -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/pixman-1 -I/usr/include/freetype2 -I/usr/include/libpng12 -I/usr/include/freetype2 -I/usr/include/cairo -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/pixman-1 -I/usr/include/freetype2 -I/usr/include/libpng12 -g -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_THREAD_SAFE -D_REENTRANT -Wl,-Bsymbolic-functions -lfltk_images -lfltk -lX11 -std=c++11 -o 's12_3_first' 's12_3_first.cpp' Simple_window.cpp Graph.cpp GUI.cpp Window.cpp

我必须添加-lfltk_images和-std=c++11

然而,现在我必须处理编译器给我的错误。为了得到一个可工作的程序,我不得不对Stroustrup在http://www.stroustrup.com/Programming/PPP2code/上提供的源代码进行多次更改。

  1. 我在Graph.h中取消了对std_lib_facilities.h的注释。
  2. 为了解决Window的歧义,我需要在Simple_window.h的第9行指定Graph_lib::Window。
  3. 当i是unsigned时,std_lib_facilities.h在第107和113行使用i<0比较(但这只是警告)。
  4. Graph.h的第159行使用了fl_color(),但编译器说应该是Fl_Color。
  5. 我需要在Point.h中取消Point构造函数的注释。
  6. Simple_window.cpp中有几个Simple_window.h的重定义。我在Simple_window.cpp中注释掉了构造函数、cb_next和wait_for_button的定义(它与Simple_window.h上的不同)。在Simple_window.h上,我注释掉了wait_for_button和next的定义。顺便说一句,wait_for_button在任何形式下都不起作用。
  7. 在GUI.cpp中,Menu的构造函数有另一个重新定义。我将其注释掉。
  8. 我更改了第12.3节示例的最后一行,从 win.wait_for_button; 到 Fl::run(); 我从附录D的示例中获取了这个代码,因为否则窗口不会通过其关闭按钮关闭。
随着所有这些变化,我最终拥有了应该有的窗口,并且该窗口可以通过“下一个”按钮或该窗口的关闭按钮关闭(使用wait_for_button后,当我尝试使用窗口的关闭按钮关闭它时,我需要从Konsole中使用Ctrl-c结束程序)。
我希望下一个人不必花费我所花费的所有时间。
编辑:好吧,检查我的系统和编译命令,我意识到有几个重复的文件夹......而且它们实际上在我的Kubuntu系统中不存在。因此,我必须在我的答案中写下我最终完成窗口所做的事情:
获取一个对象文件:
$ clang++ -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_THREAD_SAFE -D_REENTRANT -g -std=c++11 -c  Simple_window.cpp

获得我们想要的第一个程序

% clang++ -O2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_THREAD_SAFE -D_REENTRANT -Wl,-Bsymbolic-functions -lfltk_images -lfltk -lX11 -g -std=c++11 Simple_window.o Graph.o GUI.o Window.o -o z3 s12_3_first.cpp

这些要简单得多(我几乎每次需要它们时都能写出来)


2

Tomolak,当你说这个“取得进展”时,我非常高兴。不知道你是否在讽刺我,但无论如何。

我已经解决了这个问题(或者至少我已经成功地让一个带有三角形的窗口出现了)。然而,这是在注释和编辑了 Stroustrup 的大部分代码之后才做到的。我认为他的书对于初学者来说并不是很适合。我也不建议尝试使用 Linux 编译他的任何示例。

对于任何通过 Google 搜索这些问题的人,我的最终解决方案是这个命令:

$ g++ -Wno-deprecated -I/usr/local/include -I/usr/include/freetype2 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_THREAD_SAFE -D_REENTRANT -o 'windows_working' win_test.cpp Graph.cpp GUI.cpp Simple_window.cpp Window.cpp  /usr/local/lib/libfltk.a -lXext -lXft -lfontconfig -lXinerama -lpthread -ldl -lm -lX11

这包括所有有关fltk和Stroustrup的内容。我使用的程序是win_test.cpp,输出为windows_working。我通过查看fltk文件中提供的shell脚本并将其放入/usr/inc/bin中来获取此结果。它被称为fltk-config。
另外,一些有用的提示是:从fltk网站下载源代码,而不仅仅是从Stroustrup的网站中获取FL文件。然后阅读自述文件,并且在尝试书中附录D中的测试程序之前,请确切地按照说明进行操作。然后尝试他的示例代码,反复修复您发现的错误,直到它能够正常运行。 如果您认为我可以提供帮助或想了解我的解决方案是如何得出的,请给我发电子邮件(但我是一个新手,所以可能没有太大的用处)。

1

Nathan,

你的回答对我在实现Stroustrup的简单三角形程序时非常有帮助。基于你的解决方案,我想发布我的解决方案。通过对Stroustrup的代码进行一次更改,并大幅扩展编译脚本,我成功地显示了三角形。

g++ -I/usr/localinclude -I/usr/local/include/FL/images -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_THREAD_SAFE -D_REENTRANT -o 'myimplementationofstroustrup.o' 'myimplementationofstroustrup.cpp' 'Simple_window.cpp' 'Graph.cpp' 'GUI.cpp' 'Window.cpp' /usr/local/lib/libfltk.a -lpthread -ldl -lm -lX11 -L/usr/local/lib -lfltk_images -lfltk_png -lfltk_z -lfltk_jpeg -lfltk

与你的不同之处如下:

1) 我不得不给我所有的 .cpp 文件添加引号……可能是系统差异?
2) 我不得不在结尾添加了六个标志,你没有使用,否则我会得到更多的“未定义的引用”错误。
3) 由于出现了一个模糊的引用错误,我不得不在 Simple_window.h 的第17行指定“Graph_lib::Window”。这是我对 Stroustrup 的代码做出的唯一更改。


我很高兴我的回答对你有帮助。我猜不出为什么你必须在代码中添加单引号 - 也许终端正在展开其中的某些内容或其他原因... - Nathan Hutton

1

这与图形没有什么实质关系。问题似乎是你在命令行中只包含了需要编译的源文件之一。根据他的网站

g++ graph.cpp GUI.cpp Simple_window.cpp Window.cpp

看起来更像是这样。但我没有实际经验。


这里使用“已包含”是具有误导性的。 - Lightness Races in Orbit
啊,你可能是对的 - 包括这些我得到了重新定义错误 - 肯定是一个进步! - Nathan Hutton
现在出现了“在此范围内未声明strlen”的错误...所以我需要将其添加到Graph_lib范围中,是吗?似乎不太可能直到现在才被发现。我感觉自己很傻,没有包含所有的源文件。 - Nathan Hutton

0

在该书的论坛中,有一篇非常详细的解决方案:
https://groups.google.com/forum/#!msg/ppp-public/BtlzdWGuQpQ/KuDN4u-SPKgJ

基于此,我创建了一个 Github 仓库,实现了所有这些更改,并包括有关如何构建项目的详细信息:

https://github.com/cortical-iv/hello_fltk

我已经花了大约两个星期的时间来解决这个问题,我的知识(主要是从他人那里获得的)实际上都体现在那段代码中,并且会不断更新。但这是堆栈溢出,所以关于我对代码所做的更改,大多数都可以在那个论坛上找到,但由于给出链接答案并不好,所以在这里列出:

Simple_window.h

  1. 解决命名空间冲突struct Simple_window : Window { 改为
    struct Simple_window : Graph_lib::Window {

  2. 将 Simple_window() 转换为声明(删除定义)。

  3. wait_for_button() 从定义转换为声明(在 while 循环中注释掉整个 def),并将其从 void 更改为 bool,以与 Simple_window.cpp 中的定义保持一致。

  4. cb_next(...) 转换为声明

  5. void next() 转换为声明

Graph.h
1. 取消注释 #include "std_lib_facilities.h"
2. 将 fl_color 更改为 Fl_Color(~第159行)

Graph.cpp 1. 用以下内容替换 can_open: bool can_open(const string& s) // 检查名为 s 的文件是否存在并可以打开进行读取 { ifstream ff(s); return ff.is_open(); }

Point.h
1. 取消注释构造函数

Point(int xx, int yy) : x(xx), y(yy) { }    
Point() :x(0), y(0) { }

Gui.h
1. 在菜单中:Widget,将Menu(Point xy ...)更改为声明而不是定义,注释掉Widget def stuff。

完成上述操作后,如果在终端运行以下命令,则应该可以编译:

g++ -w -Wall -std=c++11 Graph.cpp Window.cpp GUI.cpp Simple_window.cpp main.cpp `fltk-config --ldflags --use-images` -o hello_fltk

如果您使用cmake安装了fltk,则也可以使用cmake / make构建示例:存储库中有一个CMakeLists.txt文件。

0

这些问题和修复的摘要对于在不同平台上使其正常工作也非常有帮助。可以下载实际已更正的代码:

FLTK编译问题 - PPP2ndEd


1
不鼓励仅提供链接的答案,因为链接的内容可能会消失。最好将相关材料复制并调整到答案中,同时保留链接以供进一步参考。 - Tom Zych
虽然通常不鼓励仅包含链接的答案,但我绝对强烈鼓励人们访问该链接,因为它是最详尽的代码修复指南。在此重现将非常困难。 Stroustrup 的代码中有太多错误,无法转化为好的答案。我正在努力解决这个问题。 - eric

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接