views:

68

answers:

4

The linker is reporting multiply defined errors for an inline function.

I have the following code in a header file:

struct Port_Pin
{
    volatile uint32_t *     port_addr_set_value;    //!< Writing the pin value here sets the pin to high.
    volatile uint32_t *     port_addr_clr_value;    //!< Writing the pin value to this port clears the pin to low.
    volatile uint32_t *     port_addr_read_value;   //!< Address to read pin value.
    volatile uint32_t *     port_addr_enable;       //!< Writing the pin value here enables the pin (for reading or writing).
    volatile uint32_t *     port_addr_disable;      //!< Writing the pin value here disables the pin.
    volatile uint32_t *     port_addr_dir_output;   //!< Writing the pin value here sets the pin as an output.
    volatile uint32_t *     port_addr_dir_input;    //!< Writing the pin value here sets the pin as an input.
    unsigned int            pin_bit_position;       //!< Zero based, where position zero is first bit position.
};

inline void
Write_Port_Pin(const struct Port_Pin *  p_port,
               uint8_t                  bit)
{
    volatile uint32_t * port_addr = 0;
    port_addr = ((bit & 1) == 0) ? p_port->port_addr_clr_value
        : p_port->port_addr_set_value;
    *port_addr = 1 << p_port->pin_bit_position;
    return;
}

I include the header file in more than one source (.c) file.

I would like to have the above function pasted inline wherever it is called.
Is there a technique for this without have multiple definitions of the function in each source file that is included? If yes, please provide example.

I need the performance optimization for an embedded platform.
Are compilers or linkers smart enough to inline functions when they are defined in other translation units?

I am using Green Hills compiler, 4.2.4 on an embedded ARM9 platform. Assume pre-2000 C language standard. This is C code not C++.

+3  A: 

inline is just a suggestion, not a command. However, in general compilers are smart enough to do the right thing ( and Green Hills has a good reputation in so far as optimizations go ).

Make the function 'static inline', which will prevent the compiler from making the symbol exportable. That should fix your multiple definition link errors... the linker is complaining that the same function is exported from several source modules.

Chris Arguin
+1 static inline is a good idea for compilers that strangely deal with inlined functions this way.
Unfortunately this will mean you carry arround an instance of the code for each module compiled using the header file. If that additional space is a problem (unlikely for a function this size)you could convert it into a macro. That would truly force inlining to occur.
torak
Carrying around an instance of the code *is* the objective of inlining. I reserve inlining only for small functions and when it is warranted through profiling or the customer requests it.
Thomas Matthews
@torak: That is nonsense. The compiler has the choice of creating a *single* static instance in *each* compilation unit (multiple instances as you say), *or* inlining the code at *each usage* (even more multiple instances). Using a macro will save no space at all - it will use as much as inlining and more than not inlining! The point is irellevant in any case since in-lining is exactly the intent.
Clifford
@Chris: just one little drawback, for such functions beware not to use a static local variable in it, or it may have surprising results. But I guess you are aware of that.
kriss
A: 

Some important notes:

It seems that you did not properly guard your header.

#ifndef NAME_H
#define NAME_H
//...contents go here...
#endif // NAME_H

This prevents multiple definitions when the header is #included more than once.

It also seems like you think that you can force a compiler to inline your function. This is not correct. A crazy and obscure compiler flag aside, the compiler will always decide if it wants to inline your function in the produced code. The inline keyword has a different meaning/purpose than what you think, see here

rubenvb
Given that this is a linker error, not a compile time error, the problem is unlikely to be the result of the header being included more than once.
torak
Actually, I didn't copy the include guard into the question, because I didn't think it was relevant. I was also thinking of not posting the structure definition also.
Thomas Matthews
A: 

In C you cannot define a function with the same name in multiple places whether it is inline or not.

The best way to handle this is to declare the function in a header (along with the structure definition it depends on, like so:

/* port_control.h */

struct Port_Pin              
{              
    volatile uint32_t *     port_addr_set_value;    //!< Writing the pin value here sets the pin to high.              
    volatile uint32_t *     port_addr_clr_value;    //!< Writing the pin value to this port clears the pin to low.              
    volatile uint32_t *     port_addr_read_value;   //!< Address to read pin value.              
    volatile uint32_t *     port_addr_enable;       //!< Writing the pin value here enables the pin (for reading or writing).              
    volatile uint32_t *     port_addr_disable;      //!< Writing the pin value here disables the pin.              
    volatile uint32_t *     port_addr_dir_output;   //!< Writing the pin value here sets the pin as an output.              
    volatile uint32_t *     port_addr_dir_input;    //!< Writing the pin value here sets the pin as an input.              
    unsigned int            pin_bit_position;       //!< Zero based, where position zero is first bit position.              
};              

/* Declare the function here so other modules know about it. */        
inline void              
Write_Port_Pin(const struct Port_Pin *  p_port,              
               uint8_t                  bit);

Then define the funtion in a .c source file in one place:

/* port_control.c */

#include "port_control.h"

inline void                     
Write_Port_Pin(const struct Port_Pin *  p_port,                     
               uint8_t                  bit)                     
{                     
    volatile uint32_t * port_addr = 0;                     
    port_addr = ((bit & 1) == 0) ? p_port->port_addr_clr_value                     
        : p_port->port_addr_set_value;                     
    *port_addr = 1 << p_port->pin_bit_position;                     
    return;                     
} 

Then #include this header file in all of the .c files that call the function.

BigEndian
Since C files are compiled separately, are the linkers intelligent enough to copy the code from the .c file containing the definition to each .c file that executes the function? Somehow I don't think your solution is any different than not using inline.
Thomas Matthews
Yes, upon reflection I think you are right. I think Chris Arguin has the right solution for your situation.
BigEndian
Your first sentence isn't true - C99, which is the first C standard to include `inline`, says: *"If all of the file scope declarations for a function in a translation unit include the `inline` function specifier without `extern`, then the definition in that translation unit is an inline definition. An inline definition does not provide an external definition for the function, and does not forbid an external definition in another translation unit."*
caf
A: 

It's not clear what you mean why "pre-2000 C language specification" - the last standard was finalised in 1999. Prior to that, inline wasn't a keyword at all.

The 1999 standard has this to say:

If all of the file scope declarations for a function in a translation unit include the inline function specifier without extern, then the definition in that translation unit is an inline definition. An inline definition does not provide an external definition for the function, and does not forbid an external definition in another translation unit. An inline definition provides an alternative to an external definition, which a translator may use to implement any call to the function in the same translation unit. It is unspecified whether a call to the function uses the inline definition or the external definition.

This means that as long as you don't have a declaration of Write_Port_Pin() with the extern qualifier, the compiler shouldn't generate an external definition of the function, so it shouldn't bother the linker. I'd submit this as a bug to your compiler vendor if I were you.

caf
Interesting; I checked into the spec myself, and as far as I can tell you are correct. I've never seen a compiler act that way, but the inline declaration does not provide an external declaration. Well, I learned something new today.
Chris Arguin