What Are Practical Applications of Weak Linking

What are practical applications of weak linking?

One use of weak linking is implementing the replaceable functions in the C++ standard. Namely:

void *operator new(std::size_t);
void *operator new(std::size_t, std::nothrow_t const &) noexcept;
void *operator new[](std::size_t);
void *operator new[](std::size_t, const std::nothrow_t&) noexcept;
void operator delete(void *) noexcept;
void operator delete(void *, std::nothrow_t const &) noexcept;
void operator delete[](void *) noexcept;
void operator delete[](void *, std::nothrow_t const &) noexcept;

These are functions which must be provided by the implementation, but if a program implements them then the program's implementation replaces or overrides the implementation's version. This is easily implemented via weak linkage.

Behaviour of weak symbol without definition

What would be a legitimate use of such weak symbols without (even an empty) implementation?

You can call the function if it is defined, and not call it otherwise:

#include <stdio.h>
void __attribute__((weak)) test(void);

int main(void) {
if (&test == NULL) {
printf("test() is not defined\n");
} else {
printf("test() is defined, calling it now ...\n");
test();
}
return 0;
}

Why does the linker use 0x0 as a placeholder, instead of issuing a diagnostic message?

The linker can't tell whether a definition will be available at runtime.

The entire reason you'd declare the function as weak external symbol is if you want to be able to handle that function's presence as optional.

If you want linker to issue diagnostics when test() is not defined, then don't declare it as a weak symbol.

Update:

What would be some other ways to use this without resorting to the LD_PRELOAD trick?

LD_PRELOAD has nothing to do with this.

Imagine your executable can work with old and new version of some_library.so. The new version defines somefunc(), and you want to call that function IFF it is present (e.g. because it is faster, or better in some other way than the "old" way of doing things).

Getting my head around the practical applications of strong and weak pointers in Objective-C

Concept

It's all about the retain counts. ARC is a convenience, to prevent developers worrying about manually retaining and releasing. At its core, a strong variable will knock up the retain count by 1, whereas a weak variable won't.

See below:

@interface Test () {
NSString* strongVariable; // Instance variables default to strong
__weak NSString* weakVariable;
}
@end

// This has a retain count of 1, it has been allocated to memory by the user, and stored in a local variable (which is strong)
NSString* str = [[NSString alloc] initWithString:@"Test"];

// The string object will now have a retain count of 2, as the strong variable has increased its retain count
strongVariable = str;

// This weak variable does **not** increase the retain count, and as such it will still be 2
weakVariable = str;

// --

// Now, lets remove some references
// This will reduce the retain count to 1, as a strong variable has lost its link
strongVariable = nil;

// This will also reduce the retain count, as another strong variable has lost it's reference. This means the retain count is 0, and the object can now be considered to not exist
str = nil;

// What happens to weakVariable?
// Because it is referencing an object with a 0 retain count, the runtime will set the value of this variable automatically to nil (iOS 5 and above).
NSLog(@"%@", (weakVariable == nil) ? @"nil" : @"Not nil") // Will print "nil"

You can't get into a situation where a strong variable is referencing an object with a retain count of 0, that defies the core concept of a strong variable. It is worth noting, along side __weak, there is __unsafe_unretained. This acts just like a weak variable, except it isn't automatically set to nil once the retain count reaches zero, meaning it will contain a pointer to a random part of memory (and will crash if you access it, you need to nil it yourself). The reason this exists is due to iOS 4 supporting ARC, but not __weak. In most cases, you'd use __weak.

The above description is just a practical glance, you can read a lot more in depth using this documentation.

Practical applications

Everything is __strong by default. If you want weak, you need to use __weak.

You would typically use weak variables when you conceptually don't want to own a particular object. Whilst a car would own its engine and wheels, it wouldn't own the driver.

Wheel* wheel;
Engine* engine;
__weak Driver* driver;

Conversely, a driver would own the Car.

Car* car;

If the car owned the driver, we would have a retain cycle. The car owns the driver, and the driver owns the car. If we were to release one, what would happen to the other? The whole concept of retain cycles outweighs the scope of this question, but you can read about it here.

The same concept applies to programming patterns, for example delegates. For a table view, the view controller would own the table view, but the table view doesn't own the view controller (Which is uses as a delegate)

//ViewController
UITableView* tableView;
tableView.delegate = self;

//UITableView
@property (nonatomic, weak) id<UITableViewDelegate> delegate;

Gotchas

One serious use of __weak is within blocks. Without them, you're at serious risk of causing retain cycles without realising. Again, this outweighs the scope of this question, but see here for more information.

Analogy to C++

Within TR1 you have the ability to use shared pointers, these allow you to put a heap allocated object within a stack allocated one, and it manages the memory for us. It does this through the use of reference counting. Everytime you pass the shared pointer to another variable, the reference count is incremented. This analogises to assigning to a strong variable in Obj-C.

Can weak symbol be resolved among libraries during linking?

When you define the SysTick_Handler in cortex_handlers.c it defines the normal entry point. But it will overwrite the weak symbol only when the linker gets a request to link the specific object file (from libOS.a).

The libHardware.a usually defines the Interrupt vector table where the SysTick_Handler is referenced. This is the only pointer this actual name comes in the game. (For the ARM it's just an entry in the vector table.) The same library already provides a weak symbol. Therefore the linker doesn't search for that symbols anymore. When you want to overwrite the symbol in cortex_handlers.c you need to reference any symbol in that file causing the linker to use cortex_handlers.o. That will kick off the weak symbol.

// cortex_handlers.c

void SysTick_Handler()
{
}

int link_my_cortex_handlers_file;

Just reference somewhere the symbol link_my_cortex_handlers_file.

Is implementing weak linkage using variable declaration/definition portable?

In lib.c, void (* const lib_callback_pnt)(int); is a declaration of an identifier for an object (in this case, a pointer) that has file scope without an initializer and without a storage-class specifier. Then C 2018 6.9.2 2 tells us:

A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.

This tells us that in standard C, lib_callback_pnt is initialized to zero even if it is defined in a different translation unit. And the fact that it behaves as if it had an initializer means it is defined in lib.c.

Furthermore, when lib_callback_pnt is defined in main2.c, this violates C 2018 6.9 5:

If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof or _Alignof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.

Note

There is some tradition in Unix of permitting the declaration of int foo; in one translation unit and int foo = 1; in another. Technically, this is not defined by the C standard, but it may be commonly available in compilers and linkers.

Why are functions generated on use of template have weak as symbol type?

Function template specializations that are generated by implicit instantiation are similar to inline functions in that if multiple translation units generate the same instantiation, the ODR is not violated. If this were not so, then templates would be almost unusable.

Weak linkage is the mechanism used to make this possible in a typical implementation.



Related Topics



Leave a reply



Submit