Controlling Symbol Visibility (在C/C++中控制符号的可见性)

45 篇文章 0 订阅
7 篇文章 0 订阅

2015-04-23 wcdj


摘要:在apple的文档中发现一篇关于符号可见性介绍的好文《Controlling Symbol Visibility》,同样适用于C/C++,在学习的过程中对关键部分顺带翻译下。


Controlling Symbol Visibility

控制符号的可见性


In ordinary C, if you want to limit the visibility of a function or variable to the current file, you apply the static keyword to it. In a shared library containing many files, though, if you want a symbol to be available in several files inside the library, but not available outside the library, hiding that symbol is more difficult. Most linkers provide convenient ways to hide or show all symbols in a module, but if you want to be more selective, it takes a lot more work.

在C语言中,可以使用static关键字限制符号(函数或变量)只对当前的文件可见,即,static对符号的限制在单个文件级别。而共享库(或动态库)可能包含一个或多个文件,如何将符号限制在库文件(模块)的级别,大多数链接器提供了将一个模块的所有符号进行隐藏或导出的方法,但这样对符号的控制会缺乏灵活性,因此,还有一些额外的工作需要我们来处理。


Using GCC 4.0 to Mark Symbol Visibility


The GCC 4.0 compiler supports new options for hiding or showing symbols and also supports a new pragma and compiler attributes for changing the visibility of symbols in your code.


从GCC 4.0 开始, 编译器提供了新的选项,用于隐藏或导出符号。并且提供了新的 pragma 和编译属性来控制符号的可见性。关于动态库的更多话题可以参考另一篇文章《Dynamic Library Programming Topics》。


Compiler Flags


GCC 4.0 supports a new flag for setting the default visibility of symbols in a file. The -fvisibility=vis compiler option lets you set the visibility for symbols in the current compilation. The value for this flag can be either default or hidden. When set to default, symbols not explicitly marked as hidden are made visible. When set to hidden, symbols not explicitly marked as visible are hidden. If you do not specify the -fvisibility flag during compilation, the compiler assumes default visibility.


GCC 4.0 提供了一个新的编译选项,用于设置符号在一个文件中默认的可见性。即,-fvisibility=vis 编译选项,且此值可以取 default 或者 hidden。当设置为 default 时,对于没有显式设置为隐藏的符号则全部设置为导出(visible);当设置为 hidden 时,对于没有显式设置为导出的符号则全部设置为隐藏(hidden)。如果在编译期间没有指定 -fvisibility 选项,则编译器默认使用 -fvisibility=default ,即,符号全部导出(visible)。


Note: The name default does not refer to compiler defaults. Like the name hidden, it comes from visibility names defined by the ELF format. A symbol with default visibility has the kind of visibility that all symbols do if no special mechanisms are used—that is, it is exported as part of the public interface.


The compiler also supports the -fvisibility-inlines-hidden flag for forcing all inline functions to be hidden. You might use this flag in situations where you want to use default visibility for most items but still want to hide all inline functions. For more information why this might be necessary for inline functions, see Visibility of Inline Functions.


Visibility Attributes


If you are compiling your code with GCC 4.0, you can mark individual symbols as default or hidden using the visibility attribute:

__attribute__((visibility("default"))) void MyFunction1() {}
__attribute__((visibility("hidden"))) void MyFunction2() {}

Visibility attributes override the value specified with the -fvisibility flag at compile-time. Thus, adding the default visibility attribute causes a symbol to be exported in all cases, whereas adding the hidden visibility attribute hides it.

Visibility attributes may be applied to functions, variables, templates, and C++ classes. If a class is marked as hidden, all of its member functions, static member variables, and compiler-generated metadata, such as virtual function tables and RTTI information, are also hidden.

显式指定符号visibility属性的优先级,要高于编译时的 -fvisibility 选项。因此,显式设置符号为 default 属性时,意味着此符号一定是导出的(visible)。 visibility属性可以用来设置,函数、变量、模板、C++类。如果一个C++类的visibility属性被设置为 hidden,那么它的成员函数、静态成员变量、编译时期产生的元数据(比如,虚函数表、RTTI信息)也都会被设置为 hidden 属性。


Note: Although template declarations can be marked with the visibility attribute, template instantiations cannot. This is a known limitation and may be fixed in a future version of GCC.


To demonstrate how these attributes work at compile-time, take a look at the following declarations:

int a(int n) {return n;}
 
__attribute__((visibility("hidden"))) int b(int n) {return n;}
 
__attribute__((visibility("default"))) int c(int n) {return n;}
 
class X
{
    public:
        virtual ~X();
};
 
class __attribute__((visibility("hidden"))) Y
{
    public:
        virtual ~Y();
};
 
class __attribute__((visibility("default"))) Z
{
    public:
        virtual ~Z();
};
 
X::~X() { }
Y::~Y() { }
Z::~Z() { }

Compiling this code with the  -fvisibility=default  flag would cause the symbols for functions  a  and  c  and classes  X  and  Z  to be exported by the library. Compiling this code with the  -fvisibility=hidden  flag would cause the symbols for the function  c  and the class  Z  to be exported.

In a large shared library, the reverse approach is usually better. Thus, it is usually better to hide all symbols and then selectively expose the ones you want clients to use.


因此,建议在一个比较大的共享库(动态库)中,编译时使用 -fvisibility=hidden 选项默认隐藏全部符号,而在代码中使用 __attribute__((visibility("default"))) 有选择的显式指定要导出的符号。这样可以极大地减少符号冲突,并且减少动态解析额外符号的成本。


To simplify the task of marking symbols for export, you might also want to define a macro with the default visibility attribute set, such as in the following example:

#define EXPORT __attribute__((visibility("default")))
 
// Always export the following function.
EXPORT int MyFunction1();

The advantage of using a macro is that if your code is also compiled on other platforms, you can change the macro to the appropriate keywords for the compilers on the other platforms.

使用宏的好处是,方便在不同的平台上移植。


Pragmas


Another way to mark symbols as default or hidden is with a new pragma in GCC 4.0. The GCC visibility pragma has the advantage of being able to mark a block of functions quickly, without the need to apply the visibility attribute to each one. The use of this pragma is as follows:

另一个设置符号可见性的方法是,在 GCC 4.0 中使用新的 pragma 关键字。优点是可以不用单独对每个符号进行设置,而是批量的进行设置。


void f() { }
 
#pragma GCC visibility push(default)
void g() { }
void h() { }
#pragma GCC visibility pop

In this example, the functions  g  and  h  are marked as default, and are therefore exported regardless of the  -fvisibility  flag, while the function  f  conforms to whatever value is set for the  -fvisibility  flag. As the names  push  and  pop  suggest, this pragma can be nested.


Reasons for Limiting Symbol Visibility

限制符号可见的原因


It is good practice to export as few symbols as possible from your dynamic shared libraries. Exporting a limited set of symbols improves program modularity and hides implementation details. Reducing the number of symbols in your libraries also decreases the footprint of your library and reduces the amount of work that must be done by the dynamic linker. With fewer symbols to load and resolve, the dynamic linker is able to get your program up and running more quickly.


即,尽可能使模块封装的更加内聚,尽可能减少模块间符号的冲突,尽可能减少动态链接时不必要的符号解析。


Reasons for Making Symbols Visible

设置符号可见的原因

Although it is likely that most C++ symbols in your shared library do not need to be visible, there are some situations where you do need to export them:

  • If your library exports a C++ interface, the symbols associated with that interface must be visible.

  • If your symbol uses runtime type identification (RTTI) information, exceptions, or dynamic casts for an object that is defined in another library, your symbol must be visible if it expects to handle requests initiated by the other library. For example, if you define a catch handler for a type in the C++ standard library, and you want to catch exceptions of that type thrown by the C++ standard library, you must make sure that your typeinfo object is visible.

  • If you expect the address of an inline function used in different code modules to be the same for each module, the function must be exported from each code module.

  • If your inline function contains a static object and you expect there to be only one copy of that object, your symbol for that static object must be visible.

即,一些场景下还是需要将"某些符号“导出给外部的。






  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用rosserial_msgs/TopicInfo来和微控制器通讯,控制两个电机实现云台在X轴和Y轴的旋转的ROS CPP代码。 ```cpp #include <ros.h> #include <std_msgs/Int32.h> #include <rosserial_msgs/TopicInfo.h> ros::NodeHandle nh; // Motor control pins const int X_STEP_PIN = 2; const int X_DIR_PIN = 3; const int Y_STEP_PIN = 4; const int Y_DIR_PIN = 5; // Motor speed control variables int x_speed = 0; int y_speed = 0; // ROS topic names const char* x_topic_name = "x_axis"; const char* y_topic_name = "y_axis"; // ROS topics ros::Publisher x_pub(x_topic_name, &x_speed); ros::Publisher y_pub(y_topic_name, &y_speed); ros::Subscriber<rosserial_msgs::TopicInfo> sub_topic_info("rosserial_topics", &topic_info_callback); void topic_info_callback(const rosserial_msgs::TopicInfo& msg) { // Check if the topic is for controlling the X-axis motor if (strcmp(msg.topic_name, x_topic_name) == 0) { // Set the speed of the X-axis motor x_speed = msg.message_id; digitalWrite(X_DIR_PIN, msg.topic_type == "std_msgs/Int32" ? x_speed > 0 : x_speed < 0); digitalWrite(X_STEP_PIN, HIGH); delayMicroseconds(abs(x_speed)); digitalWrite(X_STEP_PIN, LOW); delayMicroseconds(abs(x_speed)); } // Check if the topic is for controlling the Y-axis motor else if (strcmp(msg.topic_name, y_topic_name) == 0) { // Set the speed of the Y-axis motor y_speed = msg.message_id; digitalWrite(Y_DIR_PIN, msg.topic_type == "std_msgs/Int32" ? y_speed > 0 : y_speed < 0); digitalWrite(Y_STEP_PIN, HIGH); delayMicroseconds(abs(y_speed)); digitalWrite(Y_STEP_PIN, LOW); delayMicroseconds(abs(y_speed)); } } void setup() { // Setup motor control pins as outputs pinMode(X_STEP_PIN, OUTPUT); pinMode(X_DIR_PIN, OUTPUT); pinMode(Y_STEP_PIN, OUTPUT); pinMode(Y_DIR_PIN, OUTPUT); // Setup ROS communication nh.initNode(); nh.advertise(x_pub); nh.advertise(y_pub); nh.subscribe(sub_topic_info); // Set default motor speeds to zero x_speed = 0; y_speed = 0; } void loop() { // Publish motor speeds to ROS x_pub.publish(&x_speed); y_pub.publish(&y_speed); // Handle ROS communication nh.spinOnce(); } ``` 解释: - 首先,我们定义了ROS节点和电机控制的引脚。 - 接下来,我们定义了两个变量来存储X和Y轴电机的速度。 - 然后,我们定义了两个ROS主题,用来发布X和Y轴电机的速度。 - 我们还定义了一个ROS主题订阅器,用来接收来自微控制器的控制指令。 - 在setup()函数,我们设置了电机控制引脚为输出,并初始化了ROS节点。 - 在loop()函数,我们将电机速度发布到ROS主题,并处理来自微控制器的控制指令。 在topic_info_callback()函数,我们检查收到的ROS主题信息是否是用于控制X轴电机或Y轴电机的信息。如果是,则将速度设置为传递的消息ID,并根据速度的正负值设置电机方向。然后,我们将X或Y轴电机旋转一步,根据速度的绝对值设置延迟时间,以控制电机速度。 注意,此代码仅用于演示目的,实际使用时需要根据实际情况进行修改和调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值