【问题标题】:Selecting implementations of C++ functions using the linker使用链接器选择 C++ 函数的实现
【发布时间】:2018-11-15 14:35:25
【问题描述】:

我想通过交换它们来测试一段代码中的 C++ 函数 对于已知在工作上下文中工作的那些,其中使用链接器完成交换。 (我在 linux 下使用 C++ 和 GCC。)不幸的是,我对如何驱动链接器知之甚少,不知道如何做到这一点,或者即使它是可能的。

对我来说,主要是为了测试学生的代码和老师的代码 模型解决方案,尽管我可以想象很多其他情况 方法可能很有趣。注意学生的源代码是 可用并且可以以我喜欢的任何方式编译,但是这个来源 无法编辑。但是,老师的代码可以修改为 需要。

下面是一个简单的例子来说明这个想法。

这是老师的代码,其中的函数可以调用每个 其他方式与所示类似。这里的所有函数都是假设的 完全符合他们的规格。

#include <iostream>

using namespace std;

// model teacher program : main calls g which calls f

int f(int x) {
  cout << "in teacher-f(" << x << ")" << endl;
  return 46;
}

int g(int x) {
  cout << "in teacher-g(" << x << ")" << endl;
  int y = f(x);
  cout << "f(" << x << ") returned " << y << endl;  
  return 91;  
}

int main() {
  cout << "in teacher-main()" << endl;
  int x = 2;
  int y = g(x);
  cout << "g(" << x << ") returned " << y <<  endl;  
}

一个典型的学生代码,试图满足相同的规范, 进行测试。在我的例子中,一个“main”,几个#includes,和“using 命名空间 std;" 是预期的。

#include <iostream>

using namespace std;

// model student program : main calls g which calls f

int f(int x) {
  cout << "in student-f(" << x << ")" << endl;
  return 27;
}

int g(int x) {
  cout << "in student-g(" << x << ")" << endl;
  int y = f(x);
  cout << "f(" << x << ") returned " << y << endl;  
  return 82;  
}

int main() {
  cout << "in student-main()" << endl;
  int x = 4;
  int y = g(x);
  cout << "g(" << x << ") returned " << y <<  endl;  
  return 0;
}

我想把老师的每一个功能都一一交换来测试一下 学生单独发挥作用。

这是一种尝试,在这种情况下测试学生的 g()

g++ -c student.cpp
# (this makes student.o)
# strip f() and main() from student.o:
strip -N main -N _Z1fi student.o

# similarly for teacher, but stripping g
g++ -c teacher.cpp
strip -N _Z1gi teacher.o
g++ -o final teacher.o student.o
./final

我期望的结果是

in teacher-main()
in student-g(2)
in teacher-f(2)
f(2) returned 46
g(4) returned 82

不幸的是,我得到:

strip: not stripping symbol `_Z1fi' because it is named in a relocation

我尝试用 .so 库做类似的事情。这 删除的错误消息消失了,但不幸的是这次 老师 main 调用了我试图删除的老师 g。

g++ -shared -fPIC -o student.so student.cpp
g++ -shared -fPIC -o teacher.so teacher.cpp 
strip -N main -N _Z1fi student.so
strip -N _Z1gi  teacher.so
g++ -o final teacher.so student.so
./final

给予

in teacher-main()
in teacher-g(2)
in teacher-f(2)
f(2) returned 46
g(2) returned 91

有什么建议吗?这甚至可能吗?如果没有,有什么办法 轮到做同样的事情?如前所述,我无法编辑 student.cpp,但我 可以从其他源代码中#include 它。

谢谢 理查德

【问题讨论】:

  • 如何分别捕获教师和学生程序的输出,并与diffmeld(图形)等工具进行比较?
  • 这不符合我的要求。我想在调用教师 f() 时测试学生 g() 的结果。在学习环境中,即使他/她的 f() 不正确,我也希望能够给学生正确编码 g() 的学分。
  • 学生有什么理由需要在他们的文件中编码main?你能要求他们只编码fg,然后链接到相关老师的main吗?或者至少要求他们提交两个文件,一个带有fg,另一个带有main
  • 当然可以要求学生以特定的方式进行编码。在我的课堂上,他们都是初学者,除了最简单的指令之外的任何东西都会被大部分人误解和忽视,导致教师需要大量时间来手动编辑学生程序以使其符合要求。很难强调它们的功能必须被称为 f 和 g 而不是“myf”和“myg”,如果规范说“int”那么“double”就不行。
  • 在 99+% 的情况下剥离其 main 的源代码听起来很简单,使用正则表达式。学生不会写std::type_identity&lt;int&gt;::type main(std::void_t&lt;decltype(42)&gt;)之类的东西。

标签: c++ linker ld


【解决方案1】:

这有点违反您的要求,但我建议您更改学生代码的预期形式。不要要求他们编码main,或者要求他们在单独的编译单元中编码main,或者只是要求他们在提交之前将他们的main 重命名为main_。或者只是在编译前提交后在学生代码的顶部添加#define main main_;对于简单的任务,这就足够了。

之后,您无需从已编译的代码中删除任何内容。只需将所有你的函数放到namespace teacher,编写你自己的main 来完成所有需要的工作,并将所有代码链接在一起。更改并重新编译您的代码,为您的函数调用teacher::f,或为学生的函数调用::f

【讨论】:

  • 嗨,Petr,再一次,这确实是我目前采用并正在努力改进的被黑“解决方案”的一部分。我对 main 的宏#define hack 很好。主要问题是我必须修改学生代码并将所有学生函数调用替换为 f() 或 g() 或评估集中的任何其他函数为教师::f() 和教师::g( ) 等。这有点混乱,但如果学生使用意想不到的语法或在我只期望一个函数时使用多功能解决方案来解决问题,这可能(并且确实)会严重破坏。
【解决方案2】:

我认为唯一可能的解决方案是将其分成两个文件:

第一个文件对教师和学生应该是通用的,并且可以同时包含 mainf + g 的前向声明:

通用文件:(common.cpp)

#include <iostream>    
using namespace std;
int g(int x) ;

int f(int x) {
  cout << "in teacher-f(" << x << ")" << endl;
  return 46;
}

int main() {
  cout << "in teacher-main()" << endl;
  int x = 2;
  int y = g(x);
  cout << "g(" << x << ") returned " << y <<  endl;  
}

第二个文件应该包含g 方法(+ 前向声明到f):

老师(teacher.cpp):

#include <iostream>
using namespace std;
int f(int x);

int g(int x) {
  cout << "in teacher-g(" << x << ")" << endl;
  int y = f(x);
  cout << "f(" << x << ") returned " << y << endl;  
  return 91;  
}

学生(student.cpp):

#include <iostream>
using namespace std;
int f(int x);

int g(int x) {
  cout << "in student-g(" << x << ")" << endl;
  int y = f(x);
  cout << "f(" << x << ") returned " << y << endl;  
  return 82;  
}

现在你可以从 common.cpp + teacher.cpp 编译teacher

g++ -o teacher common.cpp teacher.cpp

和来自 common.cpp 的学生 + student.cpp

g++ -o student common.cpp student.cpp

common.cpp 甚至可以替换为静态或共享库,您可以添加标头而不是 f 的前向声明。

【讨论】:

  • 谢谢,但它不符合我的要求,也不回答原始问题。以这种方式自动编辑外国代码并且可能非常不愉快的学生代码太难了。
【解决方案3】:

这是来自劳伦斯的,回答了我的问题。我不能说便携性等。

g++ -c student.cpp
g++ -c teacher.cpp 
strip -N main student.o
objcopy -W _Z1fi student.o
objcopy -W _Z1gi  teacher.o
g++ -o final teacher.o student.o
./final

给出预期

in teacher-main()
in student-g(2)
in teacher-f(2)
f(2) returned 46
g(2) returned 82

谢谢!

PS 我第一次尝试时肯定打错了,否则剥离和削弱的顺序很重要......我说这个 objcopy -W 想法不起作用的评论现在已被删除。以上已经测试过几次了。

【讨论】:

  • 我也不是链接器专家,但我的理解是,将符号标记为“弱”允许在链接器进行符号解析时覆盖它。默认情况下,一个符号(函数或全局/静态变量)是强的。
猜你喜欢
  • 1970-01-01
  • 2021-12-19
  • 1970-01-01
  • 1970-01-01
  • 2015-06-30
  • 2014-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多