标签: cpp
-
作者帖子
-
最近由于更换IDE和CLI,需要将程序全部重写,于是也就考虑将之前不规范的代码整理成规范内容.
原IDE:VEXcode Pro V5
现IDE:PROS Editor(均为VEX机器人专用IDE)
存疑内容:.h文件一般用于声明常量和函数 而 cpp文件一般用于表达函数逻辑.
那我在引用.h文件时,编译器发现了声明的函数,它如何去寻找函数的逻辑内容?
是去寻找一个cpp文件吗?要让两者关联有什么格式要求吗?
常见.h的格式是什么样的,全局定义吗?
为了防止重复编译还有哪些办法?除了#pragma once
我个人已经很多年不写C++了,以下内容只保证总体上正确:
编译器如何寻找h文件对应的cpp文件
这个涉及到C++编译器的编译过程:
- 预处理
- 编译
- 链接
在预处理阶段,所有使用include宏标记的内容都会被原封不动的复制到当前文件中。
比如说以下代码:
include "hello.h" int main() { hello(); return 0; }
经过预处理后会变成这样:
int main(); int main() { hello(); return 0; }
可以发现,其实根本不存在所谓的“h文件与cpp文件关联”,因为h文件根本就是等于不存在,在预处理过程中它就会被全部复制到引用它的cpp中。
关于include宏的介绍见GCC官方文档:https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
在其后的编译过程中,每个cpp文件会被单独编译,并各自生成二进制文件。如果你没在每个cpp文件头部声明要引用的类、函数的定义,则这个阶段就会抛出函数、类未定义的错误。
最后的链接过程可以理解成是将多个编译后的cpp文件关联成一个整体,对于在同一个命名空间和作用域中的函数、类、变量,其链接后就是全局公用的。在这个阶段如果函数的逻辑部分未发现的话g++编辑器会抛出错误。
常见.h的格式是什么样的
如果你的代码不包含对外提供的接口的话可以不写h文件,而是使用extern关键字声明该函数、变量、类在此cpp文件外部定义。
当然,不怕麻烦的话给每个cpp文件写一个h文件也可以,或者也可以把多个cpp文件的定义塞到一个h文件里。
如果有一些全局配置信息的话可以写一个类似config.h这种文件,其中只包含一些常量的定义,就好像WordPress的wp-config.php一样。
防止重复编译
在Linux内核中多使用以下宏来避免重复包含:
#ifndef FOOP_H #define FOOP_H int foo(int a); #endif
比如这个关于ECC加密算法的头文件:
https://github.com/torvalds/linux/blob/master/include/crypto/ecc_curve.h
抱歉我还是不太懂你的意思。
我写.h文件的目的是让给机器人下载的主函数文件尽量简洁。
由于机器人的特殊性,我的函数要被反复反复利用。
所以.h文件即起到函数分类的作用 也起到简洁代码的作用。
我要是不想把逻辑写在我的主函数文件里,也不应该写在.h里面,那我应该把函数逻辑写在哪里,引用这个.h文件时,编译器才能找到它的逻辑。
你可以把函数逻辑写在任意cpp文件里,把函数声明写在任意h文件里(h文件和cpp文件没有任何关联关系,只要函数声明和定义能对应起来就行)
Adding timestamp [OK]
Linking hot project with ./bin/cold.package.elf and libc,libm,libpros,okapilib [ERRORS]
/usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:25: multiple definition of `LED’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:25: first defined here
/usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:22: multiple definition of `IS’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:22: first defined here
/usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:19: multiple definition of `RB’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:19: first defined here
/usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:18: multiple definition of `RF’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:18: first defined here
/usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:16: multiple definition of `ML’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:16: first defined here
/usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:15: multiple definition of `BL’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:15: first defined here
/usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:14: multiple definition of `FL’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:14: first defined here
/usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:13: multiple definition of `LB’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:13: first defined here
/usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:12: multiple definition of `LF’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:12: first defined here
/usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:9: multiple definition of `Ctrl2′; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:9: first defined here
/usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:8: multiple definition of `Ctrl1′; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:8: first defined here
collect2: error: ld returned 1 exit status
make: *** [bin/hot.package.elf] Error 1
ERROR – pros.cli.build:build_compile_commands – Failed to make project: Exit Code 2
Error: Failed to build重复定义错误
我重复包含了config.h文件
做了
#ifndef CONFIG_H
#define CONFIG_H
#pragma once的处理
但是还是无法编译
代码贴上来看看。
记得用编辑器的插入代码功能贴,别直接复制粘贴
config.h
_Pragma("once") #pragma once #ifndef _CONFIG_H_ #define _CONFIG_H_ #include "main.h" //---Declare Devices--- //Ctrl Controller Ctrl1(CONTROLLER_MASTER); Controller Ctrl2(CONTROLLER_PARTNER); //Motor fwd Motor LF(12,E_MOTOR_GEARSET_18,false); Motor LB(13,E_MOTOR_GEARSET_18,false); Motor FL(15,E_MOTOR_GEARSET_18,false); Motor BL(16,E_MOTOR_GEARSET_18,false); Motor ML(19,E_MOTOR_GEARSET_18,false); //Motor rev Motor RF(17,E_MOTOR_GEARSET_18,true); Motor RB(18,E_MOTOR_GEARSET_18,true); //Imu Imu IS (14); //3-Wire LED ADIDigitalOut LED ('A'); #endif
ctrl_func.h
#pragma once #ifndef _CTRL_FUNC_H #define _CTRL_FUNC_H #include "main.h" void STOP_MOVE(); void STOP_LIFT(); void LOCK(); void Ctrl_L(int ctrl_power); #endif
ctrl_func.cpp
#include "main.h" #include "config.h" long long shin_count = 0; void Ctrl_Shin(bool status) { shin_count++; if (status) { if (shin_count % 10 == 0) { LED.set_value(1); } if (shin_count % 10 == 5) { LED.set_value(0); } } else { LED.set_value(0); } } void STOP_MOVE() { LF.set_brake_mode(E_MOTOR_BRAKE_BRAKE); LB.set_brake_mode(E_MOTOR_BRAKE_BRAKE); RF.set_brake_mode(E_MOTOR_BRAKE_BRAKE); RB.set_brake_mode(E_MOTOR_BRAKE_BRAKE); } void STOP_LIFT() { FL.set_brake_mode(E_MOTOR_BRAKE_HOLD); ML.set_brake_mode(E_MOTOR_BRAKE_HOLD); BL.set_brake_mode(E_MOTOR_BRAKE_HOLD); } void LOCK() { STOP_MOVE(); STOP_LIFT(); } void Ctrl_L(int ctrl_power) { if (ctrl_power == 0) { LF.set_brake_mode(E_MOTOR_BRAKE_BRAKE); LB.set_brake_mode(E_MOTOR_BRAKE_BRAKE); } else { LF.move(ctrl_power); LB.move(ctrl_power); } }
main.cpp
#include "main.h" #include "config.h" #include "ctrl_func.h" /** * Runs initialization code. This occurs as soon as the program is started. * * All other competition modes are blocked by initialize; it is recommended * to keep execution time for this mode under a few seconds. */ void initialize() { LED.set_value(1); LF.set_zero_position(0); LB.set_zero_position(0); RF.set_zero_position(0); RB.set_zero_position(0); FL.set_zero_position(0); BL.set_zero_position(0); ML.set_zero_position(0); IS.reset(); LED.set_value(0); } /** * Runs while the robot is in the disabled state of Field Management System or * the VEX Competition Switch, following either autonomous or opcontrol. When * the robot is enabled, this task will exit. */ void disabled() { LOCK(); } /** * Runs after initialize(), and before autonomous when connected to the Field * Management System or the VEX Competition Switch. This is intended for * competition-specific initialization routines, such as an autonomous selector * on the LCD. * * This task will exit when the robot is enabled and autonomous or opcontrol * starts. */ void competition_initialize() {} /** * Runs the user autonomous code. This function will be started in its own task * with the default priority and stack size whenever the robot is enabled via * the Field Management System or the VEX Competition Switch in the autonomous * mode. Alternatively, this function may be called in initialize or opcontrol * for non-competition testing purposes. * * If the robot is disabled or communications is lost, the autonomous task * will be stopped. Re-enabling the robot will restart the task, not re-start it * from where it left off. */ void autonomous() {} /** * Runs the operator control code. This function will be started in its own task * with the default priority and stack size whenever the robot is enabled via * the Field Management System or the VEX Competition Switch in the operator * control mode. * * If no competition control is connected, this function will run immediately * following initialize(). * * If the robot is disabled or communications is lost, the * operator control task will be stopped. Re-enabling the robot will restart the * task, not resume it from where it left off. */ void opcontrol() { Controller master(E_CONTROLLER_MASTER); Motor left_mtr(1); Motor right_mtr(2); while (true) { lcd::print(0, "%d %d %d", (lcd::read_buttons() & LCD_BTN_LEFT) >> 2, (lcd::read_buttons() & LCD_BTN_CENTER) >> 1, (lcd::read_buttons() & LCD_BTN_RIGHT) >> 0); int left = master.get_analog(ANALOG_LEFT_Y); int right = master.get_analog(ANALOG_RIGHT_Y); left_mtr = left; right_mtr = right; delay(10); } }
main.h
/** * file main.h * * Contains common definitions and header files used throughout your PROS * project. * * Copyright (c) 2017-2021, Purdue University ACM SIGBots. * All rights reserved. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef _PROS_MAIN_H_ #define _PROS_MAIN_H_ /** * If defined, some commonly used enums will have preprocessor macros which give * a shorter, more convenient naming pattern. If this isn't desired, simply * comment the following line out. * * For instance, E_CONTROLLER_MASTER has a shorter name: CONTROLLER_MASTER. * E_CONTROLLER_MASTER is pedantically correct within the PROS styleguide, but * not convienent for most student programmers. */ #define PROS_USE_SIMPLE_NAMES /** * If defined, C++ literals will be available for use. All literals are in the * pros::literals namespace. * * For instance, you can do
4_mtr = 50
to set motor 4's target velocity to 50 */ #define PROS_USE_LITERALS #include "api.h" #include <bits/stdc++.h>//Added by Sukanu /** * You should add more #includes here */ //#include "okapi/api.hpp" //#include "pros/api_legacy.h" /** * If you find doing pros::Motor() to be tedious and you'd prefer just to do * Motor, you can use the namespace with the following commented out line. * * IMPORTANT: Only the okapi or pros namespace may be used, not both * concurrently! The okapi namespace will export all symbols inside the pros * namespace. */ using namespace pros; using namespace std;//Added by Sukanu // using namespace pros::literals; // using namespace okapi; /** * Prototypes for the competition control tasks are redefined here to ensure * that they can be called from user code (i.e. calling autonomous from a * button press in opcontrol() for testing purposes). */ #ifdef __cplusplus extern "C" { #endif void autonomous(void); void initialize(void); void disabled(void); void competition_initialize(void); void opcontrol(void); #ifdef __cplusplus } #endif #ifdef __cplusplus /** * You can add C++-only headers here */ #include <iostream> #endif #endif // _PROS_MAIN_H_先把代码改成最简版本。就只留下config.h引入相关的代码,然后config.h里面只留下一个常量,去掉其他所有注释和无关代码。目前这一大摊把我眼睛看瞎了,尤其是你这代码依赖特定环境,又不能在本地调试。
另外,在其他.h里面引入main.h是什么操作?
最后你的项目目录是/Users/sukanu/L3313B?
在其他.h中引入main.h是因为其他.h的定义也需要main.h中机器人函数库的支持。
我的项目目录没错,就是这个。
我怀疑是不是因为我同时在main.cpp和ctrl_func.cpp都引用了config.h导致不同文件无法只编译一次。
这个问题怎么破?
先把代码精简成最小demo,见我前面的回复。
排错得先移除干扰因素
已经自行排查 找到原因
Cpp的仅编译一次似乎是针对单一文件的
一旦我建立两个cpp源文件 好像就必定编译config.h两次
由于机器人的设备定义是依赖现有机器人CLI来实现的
导致重复定义必报错
这好像是无解的.
确实是只能单个文件,其针对的是循环依赖的场景。因为预处理阶段是针对每个.cpp文件单独进行的,所以对于多个cpp文件会失效。
对于全局变量,你可以这样处理:
只在main.cpp中包含config.h,然后在其他cpp文件中使用extern关键字来标识要访问的变量,这样就可以了。
-
作者帖子
- 哎呀,回复话题必需登录。