Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Define object in header file

78 views
Skip to first unread message

Frederick Virchanza Gotham

unread,
Oct 14, 2022, 11:51:04 AM10/14/22
to
Usually we put all the declarations of objects in a header file, and all the definitions of objects in a source file.

At the moment though I'm writing a 'universal header file' and so I want to put everything in the header file. There will be no accompanying source file.

With the latest versions of the C++ Standard, we can use the 'inline' keyword to define a variable inside a header file, however I need to write my header file to compile with a C++03 compiler. So I resort to the following in the header file:


SomeType &GetObj(void)
{
SomeType obj;
return obj;
}


This works fine -- however there is a problem with the Texas Instruments compiler. The manual says that where you have a static variable inside an inline function, there are multiple copies of the static variable (one for each translation unit).

So now I'm trying to devise a way of making this work for the Texas Instruments compiler too, maybe something like the following:


SomeType &GetObj(void)
{
__asm("ABC: .bss mybuf, 64);

return *static_cast<SomeType*>(static_cast<void*>(buf));
}


I still haven't got this working yet. Anyone got any ideas?

David Brown

unread,
Oct 14, 2022, 1:43:21 PM10/14/22
to
On 14/10/2022 17:50, Frederick Virchanza Gotham wrote:
> Usually we put all the declarations of objects in a header file, and all the definitions of objects in a source file.
>
> At the moment though I'm writing a 'universal header file' and so I want to put everything in the header file. There will be no accompanying source file.
>
> With the latest versions of the C++ Standard, we can use the 'inline' keyword to define a variable inside a header file, however I need to write my header file to compile with a C++03 compiler. So I resort to the following in the header file:
>
>
> SomeType &GetObj(void)
> {
> SomeType obj;
> return obj;
> }
>
>
> This works fine -- however there is a problem with the Texas Instruments compiler. The manual says that where you have a static variable inside an inline function, there are multiple copies of the static variable (one for each translation unit).
>

Are you sure? In C, that is the case (you are only allowed non-const
static local variables in static inline functions in C). But not in
C++. However, as I have mentioned before, TI sometimes considers the
language standards as more of a set of guidelines than rules you can
rely upon.

> So now I'm trying to devise a way of making this work for the Texas Instruments compiler too, maybe something like the following:
>
>
> SomeType &GetObj(void)
> {
> __asm("ABC: .bss mybuf, 64);
>
> return *static_cast<SomeType*>(static_cast<void*>(buf));
> }
>
>
> I still haven't got this working yet. Anyone got any ideas?

Check that the TI compiler is really that bad for C++.

Don't mess with inline assembly like this - it will lead to madness.
The assembly details are different for each target, the linking is going
to add complications, and TI's tools might not clear the .bss segment
anyway.

Try:

enum objIdentifiers {
obj1, obj2, objX,
};

template <objIdentifiers i, class T> T& getObj()
{
static T obj;
return obj;
}

Then you use it by calling :

getObj<obj1, SomeType>();

Linking should merge all the static objects for the same template
function. Use sensible names in the enumeration, of course. And
override the template function for specific instances if you need
non-default construction of the object.

You can also add:

static SomeType& this_object = getObj<objX, SomeType>;

and refer simply to "this_object".


I haven't tried any of this in real code, and with TI's tools you must
always be sceptical until you have convinced yourself it works in
practice. But the principle is correct - static locals in template
functions are merged across the program link when the template
parameters are the same, and separate when the template parameters do
not match. The C++17 "inline variable" was added just to make this
neater and simpler.

Andrey Tarasevich

unread,
Oct 14, 2022, 2:03:31 PM10/14/22
to
On 10/14/2022 8:50 AM, Frederick Virchanza Gotham wrote:
> Usually we put all the declarations of objects in a header file, and all the definitions of objects in a source file.
>
> At the moment though I'm writing a 'universal header file' and so I want to put everything in the header file. There will be no accompanying source file.
>
> With the latest versions of the C++ Standard, we can use the 'inline' keyword to define a variable inside a header file, however I need to write my header file to compile with a C++03 compiler. So I resort to the following in the header file:
>
>
> SomeType &GetObj(void)
> {
> SomeType obj;
> return obj;
> }
>
>
> This works fine -- however there is a problem with the Texas Instruments compiler. The manual says that where you have a static variable inside an inline function, there are multiple copies of the static variable (one for each translation unit).

Um... I see neither `inline` nor `static` in the above code.

Anyway, another way to emulate inline variables in earlier versions of
C++ would be to implement it as a static member of a dummy template class

template <typename DUMMY = void> struct InlinesImpl
{
static int a; // Declaration
};

template <typename DUMMY> int InlinesImpl<DUMMY>::a = 42;
// Definition

typedef InlinesImpl<> Inlines;
// Just for convenience

// ... and now we can use `Inlines::a` as a global inline variable
// in your header file

What does your TI compiler think about that?

--
Best regards,
Andrey


Alf P. Steinbach

unread,
Oct 14, 2022, 2:52:25 PM10/14/22
to
On 14 Oct 2022 17:50, Frederick Virchanza Gotham wrote:
> [snip]
> there is a problem with the Texas
> Instruments compiler. The manual says that where you have a static
> variable inside an inline function, there are multiple copies of the
> static variable (one for each translation unit).

I would first of all /test/ that. I would be a weird compiler to have
such a bug and have it documented as intended behavior.

> So now I'm trying to devise a way of making this work for the Texas
> Instruments compiler too, maybe something like the following:
>
>
> SomeType &GetObj(void) { __asm("ABC: .bss mybuf, 64);
>
> return *static_cast<SomeType*>(static_cast<void*>(buf)); }
>
>
> I still haven't got this working yet. Anyone got any ideas?

C++03 had two mechanisms for discarding duplicates at link time:
`inline` for functions, and `template` in general.

Unless the compiler has a bug (documented or not) for that too you can
write e.g.


namespace my {
using std::string;

template< class >
struct Foo_
{
static string message;
};

template< class Dummy >
string Foo_<Dummy>::message = "unassigned";

inline string& message() { return Foo_<void>::message; }
} // namespace my


- Alf

Marcel Mueller

unread,
Oct 14, 2022, 2:57:12 PM10/14/22
to
Am 14.10.22 um 17:50 schrieb Frederick Virchanza Gotham:
> Usually we put all the declarations of objects in a header file, and all the definitions of objects in a source file.
>
> At the moment though I'm writing a 'universal header file' and so I want to put everything in the header file. There will be no accompanying source file.

Don't do that.


> With the latest versions of the C++ Standard, we can use the 'inline' keyword to define a variable inside a header file, however I need to write my header file to compile with a C++03 compiler. So I resort to the following in the header file:
>
>
> SomeType &GetObj(void)
> {
> SomeType obj;
> return obj;
> }

This is undefined behavior because of a dangling reference. The compiler
should warn you about this fault.


> This works fine -- however there is a problem with the Texas Instruments compiler. The manual says that where you have a static variable inside an inline function, there are multiple copies of the static variable (one for each translation unit).
>
> So now I'm trying to devise a way of making this work for the Texas Instruments compiler too, maybe something like the following:

If the TI compiler has no weak linker, you will _never_ succeed.
You cannot deduplicate storage without global symbols. And you cannot
have global symbols defined in different translation units unless you
have a weak linker.

As long as you are only talking about code a compiler w/o weak symbols
might simply duplicate the code for each translation unit. But this does
not work for storage.

> SomeType &GetObj(void)
> {
> __asm("ABC: .bss mybuf, 64);
>
> return *static_cast<SomeType*>(static_cast<void*>(buf));
> }
>
> I still haven't got this working yet. Anyone got any ideas?

This will either generate one slot for each translation unit if the
symbol ABC is not global or a linker error if the symbol is global.

To get the desired result the symbol needs to be weak and the storage
needs its own segment in the object file.


You have two options:
- Either do not use static storage at all. Instance storage is managed
by the one who calls the constructor.
- Or use a dedicated translation unit for your storage as anyone else
does. The latter could be placed in a static library to simplify things.


Marcel

Chris M. Thomasson

unread,
Oct 14, 2022, 5:41:02 PM10/14/22
to
On 10/14/2022 11:56 AM, Marcel Mueller wrote:
> Am 14.10.22 um 17:50 schrieb Frederick Virchanza Gotham:
>> Usually we put all the declarations of objects in a header file, and
>> all the definitions of objects in a source file.
>>
>> At the moment though I'm writing a 'universal header file' and so I
>> want to put everything in the header file. There will be no
>> accompanying source file.
>
> Don't do that.
[...]

Perhaps I am misunderstanding your main point, however, there are some
nice "header only" libraries. GLM comes to mind:

https://github.com/g-truc/glm

Frederick Virchanza Gotham

unread,
Oct 14, 2022, 7:14:55 PM10/14/22
to
On Friday, October 14, 2022 at 7:52:25 PM UTC+1, Alf wrote:

> inline string& message() { return Foo_<void>::message; }


Thanks Alf, the following compiled and linked fine for me:

template<class> struct Foo { static SimpleCrypto128 obj; };
template<class Dummy> SimpleCrypto128 Foo<Dummy>::obj;
inline SimpleCrypto128 &Scrypt128(void) { return Foo<void>::obj; }

I hadn't bothered trying using templates for this, because I figured if the compiler couldn't handle static variables in simple inline functions, then how could it cope with a template function. But anyway it has compiled and linked for me. Of course I'll have to test it to make sure there's only one object, but so far it looks good.

As for the peculiarities of the Texas Instruments compiler, well they at least have a finite list of its non-compliance. I've copy-pasted the following from the compiler manual:

6.2 Characteristics of TMS320C28x C++

The C28x compiler supports C++ as defined in the ANSI/ISO/IEC 14882:2003 standard (C++03), including these
features:

• Complete C++ standard library support, with exceptions noted below.
• Templates
• Exceptions, which are enabled with the --exceptions option; see Section 6.6.
• Run-time type information (RTTI), which can be enabled with the --rtti compiler option.

The compiler supports the 2003 standard of C++ as standardized by the ISO. However, the following features
are not implemented or fully supported:

• The compiler does not support embedded C++ run-time-support libraries.
• The library supports wide chars (wchar_t), in that template functions and classes that are defined for char are
also available for wchar_t. For example, wide char stream classes wios, wiostream, wstreambuf and so on
(corresponding to char classes ios, iostream, streambuf) are implemented. However, there is no low-level file
I/O for wide chars. Also, the C library interface to wide char support (through the C++ headers <cwchar> and
<cwctype>) is limited as described above in the C library.
• If the definition of an inline function contains a static variable, and it appears in multiple compilation units
(usually because it’s a member function of a class defined in a header file), the compiler generates multiple
copies of the static variable rather than resolving them to a single definition. The compiler emits a warning
(#1369) in such cases.
• The export keyword is not implemented.

Marcel Mueller

unread,
Oct 15, 2022, 7:41:09 AM10/15/22
to
Am 14.10.22 um 23:40 schrieb Chris M. Thomasson:
> Perhaps I am misunderstanding your main point, however, there are some
> nice "header only" libraries. GLM comes to mind:

Of course, but when the OP has a compiler that does _not_ support the
required features this is not an option.


Marcel

Chris M. Thomasson

unread,
Oct 15, 2022, 3:47:33 PM10/15/22
to
Touche Marcel. I am guilty of an error comprised of posting without
properly reading prior context. Sorry everybody. ;^o
0 new messages