Weird issue with external constants in constructor of template classes - c++

I have the following three source files:
main.cc:
#include "constants.h"
#include <cstdlib>
#include <iostream>
template <std::size_t n> class Foo {
public:
static const double bar;
Foo();
};
template <std::size_t n> const double Foo<n>::bar = 4*pi;
template <std::size_t n> Foo<n>::Foo() { std::cout << "ctor: " << bar << std::endl; }
const Foo<42> baz;
int main(void)
{
std::cout << "main(): " << Foo<42>::bar << std::endl;
std::cout << "main(): " << baz.bar << std::endl;
return 0;
}
constants.cc:
#include "constants.h"
const double pi = 3.1415;
constants.h:
#ifndef CONSTANTS_H
#define CONSTANTS_H
extern const double pi;
#endif
and when I compile and link everything and run the executable I get:
ctor: 0
main(): 12.566
main(): 12.566
What gives? How comes the constructor of Foo<42> can't see the correct value of pi? This seems to only happen if Foo is a template, and only if pi is defined in a different file.
For what it's worth I'm using g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0. All of --std={c++,gnu++}{98,03,11,14,17} give the same results.
TIA for your answers.

Related

MSVC compilation failure on explicit variadic template function instantiation

I've distilled my problem to a small example, pardon the macros. It seems a similar problem from this post is no longer an issue in VS and compiles fine. I believe I have a more specialized version of this problem that hasn't been fixed but want to make sure I'm not missing something. The following code compiles in GCC and runs expectedly, but gives error C2893 (Failed to specialize function template) in VS:
Macros.h:
#define If(x) \
template<class T,class...Args, typename std::enable_if<std::is_same<T, x>::value>::type* = nullptr>
#define Do void Func(Args... args)
Definition.cpp:
#include <string>
#include <iostream>
#include "Macros.h"
using namespace std;
int answer = 42;
double pie = 3.14;
string s = "Hello World";
// Function Definitions
If(int) Do { cout << answer << endl; }
If(double) Do { cout << pie << endl; }
If(string) Do { cout << s << endl; }
// Explicit Instantiations
template void Func<int>(int, double, string);
template void Func<double>();
template void Func<string>();
Usage.cpp:
#include <string>
#include <type_traits>
#include "Macros.h"
// Template Function Declaration
If(T) Do;
int main() {
using namespace std;
Func<int>(5, 2.0, string("hello"));
Func<double>();
Func<string>();
}
As with the example in the other post it works correctly if the instantiations come from actual use in a function. Doing so is simple with this example but not so simple with my code.

c++ extern class does not name a type

I'm trying to declare a class object as extern but am getting the below error:
g++ a1.cpp -std=c++11
In file included from b1.h:5:0,
from a1.cpp:2:
c1.h:6:8: error: ‘b1’ does not name a type
extern b1 obj_b1;
^
I have looked in
Issue declaring extern class object
and
'[Class name]' does not name a type in C++
and I THINK I am following the steps mentioned there. But couldn't find what's the issue.
Files are :
a1.cpp
#include<iostream>
#include "b1.h"
b1 obj_b1;
int main(){
//access object from class B
std::cout << " test " << std::endl;
std::cout << " obj_b1 value is " << obj_b1.value << std::endl;
obj_b1.value = 6;
return 0;
}
b1.h
#ifndef CLASS_B1
#define CLASS_B1
#include "c1.h"
class b1{
public:
int value=5;
int print_value();
};
#endif
b1.cpp
#include <iostream>
#include "b1.h"
int b1::print_value(){
std::cout << "value in b1 is " << value << std::endl;
}
c1.h
#ifndef CLASS_C1
#define CLASS_C1
#include "b1.h" // this is an attempt to fix issue, but didnt work
extern b1 obj_b1; // Is there a better place to declare this ?
class c1 {
private:
int c1_value=10;
int c1_print_value();
};
#endif
c1.cpp
#include<iostream>
#include "c1.h"
int c1::c1_print_value()
{
std::cout << "in c1 , value is " << c1_value << std::endl;
std::cout << " obj_b1.value is " << obj_b1.value << std::endl;
return 0;
}
I can not understand why the compiler complains about b1, when I have added b1.h just above the extern declaration. Can someone help to solve the issue ?
b1.h includes c1.h, and c1.h includes b1.h. This is is a mess. By using the #indef/#define combo, you have prevented an infinite recursion, but it is still a mess.
obj_b1 doesn't have anything to do with class c1, so remove extern b1 obj_b1; from c1.h.
Now c1.h doesn't depend on anything in b1.h, so you can remove #include "b1.h" from c1.h.
An for similar reason, you should remove #include "c2.h" from b1.h.
On the other hand c2.cpp does depend on obj_b1 (assuming obj1.name is a typo, and should be obj_b1.name), so you should put extern b1 obj_b1; in b1.h and #include "b1.h" in c2.cpp.
For some extra cleanup, move b1 obj_b1; from a1.cpp to b1.cpp

Nifty counter in precompiled header does not work

To fix a problem caused by the static (de-)initialization order fiasco I tried to use the Nifty Counter idiom aka Schwartz Counter. I noticed, however, that it does not work if the header file containing the static initializer is included inside Visual Studio's precompiled header. In such a case I see in the debugger, that the constructor of the static initializer is called after the constructor of the other static object.
If I declare an additional static initializer right after #include "stdafx.h" or edit the precompiled header to not contain the declaration it works as expected.
Any ideas what might cause this?
EDIT
I was finally able to reproduce this problem in a new dummy project:
Foo.cpp
#include "stdafx.h"
#include "Foo.h"
#include <cassert>
#include <iostream>
#ifdef NIFTY
static int SchwartzCounter; // zero initialized at load time
static typename std::aligned_storage<sizeof(Foo), alignof (Foo)>::type foo_buf;
Foo& foo = reinterpret_cast<Foo&>(foo_buf);
#else
Foo foo;
#endif
Foo::Foo()
{
std::cout << __func__ << std::endl;
}
Foo::~Foo()
{
std::cout << __func__ << std::endl;
}
void Foo::doSomething()
{
std::cout << __func__ << std::endl;
assert(x == 42);
}
#ifdef NIFTY
FooInitializer::FooInitializer()
{
std::cout << __func__ << std::endl;
if (SchwartzCounter++ == 0)
{
new (&foo) Foo();
}
}
FooInitializer::~FooInitializer()
{
std::cout << __func__ << std::endl;
if (--SchwartzCounter == 0)
{
(&foo)->~Foo();
}
}
#endif
Foo.h
#pragma once
class Foo
{
public:
Foo();
~Foo();
void doSomething();
private:
int x = 42;
};
#ifdef NIFTY
extern Foo& foo;
static struct FooInitializer {
FooInitializer();
~FooInitializer();
} fooInitializer;
#else
extern Foo foo;
#endif
Bar.cpp
#include "stdafx.h"
#include "Foo.h"
#include "Bar.h"
#include <cassert>
#include <iostream>
#ifdef NIFTY
static int SchwartzCounter; // zero initialized at load time
static typename std::aligned_storage<sizeof(Bar), alignof (Bar)>::type bar_buf;
Bar& bar = reinterpret_cast<Bar&>(bar_buf);
#else
Bar bar;
#endif
Bar::Bar()
{
std::cout << __func__ << std::endl;
foo.doSomething();
}
Bar::~Bar()
{
std::cout << __func__ << std::endl;
}
void Bar::doSomething()
{
std::cout << __func__ << std::endl;
assert(x == 42);
}
#ifdef NIFTY
BarInitializer::BarInitializer()
{
std::cout << __func__ << std::endl;
if (SchwartzCounter++ == 0)
{
new (&bar) Bar();
}
}
BarInitializer::~BarInitializer()
{
std::cout << __func__ << std::endl;
if (--SchwartzCounter == 0)
{
(&bar)->~Bar();
}
}
#endif
Bar.h
#pragma once
class Bar
{
public:
Bar();
~Bar();
void doSomething();
private:
int x = 42;
};
#ifdef NIFTY
extern Bar& bar;
static struct BarInitializer {
BarInitializer();
~BarInitializer();
} barInitializer;
#else
extern Bar bar;
#endif
stdafx.h
#pragma once
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
// If the following define is commented out, the nifty counter idiom is not used
#define NIFTY
// If the following include is commented out while the nifty counter idiom is used, the initialization order is correct
#include "Foo.h"

How can I pack a template class?

I have use a packed template class. According to this I should wrap it with #pragma pack as in this example:
#include <iostream>
#pragma pack(1)
template<typename X>
struct S {
int a;
X b;
};
#pragma pack()
int main()
{
std::cout << sizeof(S<char>) << "\n";
}
Using gcc-4.4.7 on my centOS6, the program output 8, which means pack(1) didn't work. Then I tried to change main() to:
int main()
{
std::cout << sizeof(S<char>) << "\n";
#pragma pack(1)
std::cout << sizeof(S<char>) << "\n";
#pragma pack()
}
Surprisingly it still didn't work. If I comment out the first line in main() then it works. So my question is how to use pack on a template class instantiation? Is there anyway to use both packed and unpacked template class in the same program?
Update: If I use gcc-5.2.1, then it output 5 correctly. Also for the following program, I get opposite results (5 with gcc-4.4.7 and 8 with 5.2.1):
template<typename X>
struct S {
int a;
X b;
};
int main()
{
#pragma pack(1)
std::cout << sizeof(S<char>) << "\n";
#pragma pack()
}
Why do gcc-4.4.7 and 5.2.1 behave differently? Which one is correct? Is there anyway to get consistent result with different versions of gcc?
It works for me
#include <iostream>
#pragma pack(1)
namespace Packed
{
template<typename X>
struct S {
int a;
X b;
};
template struct S<char>;
}
#pragma pack()
namespace NotPacked
{
template<typename X>
struct S {
int a;
X b;
};
template struct S<char>;
}
int main()
{
std::cout << sizeof(Packed::S<char>) << "\n";
std::cout << sizeof(NotPacked::S<char>) << "\n";
}
You can put definition of S to header (but without #pragma once) and avoid code duplication:
#pragma pack(1)
namespace Packed
{
#include "S.h"
}
#pragma pack()
namespace NotPacked
{
#include "S.h"
}

How do I declare a const in a header?

I want to test defining a const in a header and use it in functions, then call it. However I get the error, I added include guards which doesn't help. Error is: LNK1169: One or more defined multiply symbols found. How can i do it in a nother way? Is declaring const in .h and defining this const in .cpp and then including this .cpp in all other .cpps the only solution?
Header
#ifndef STORY
#define STORY
const int x = 4;
#endif
.cpp
#include <iostream>
#include "8-04.h"
void func1()
{
int w = x;
std::cout << "func1 " << w << std::endl;
}
.cpp
#include <iostream>
#include "8-04.h"
void func2()
{
int z = x;
std::cout << "func2 " << z << std::endl;
}
main
#include <iostream>
#include "8-04.h"
#include "8-04first.cpp"
#include "8-04second.cpp"
using namespace std;
int main()
{
func1();
func2();
}
The problem is that each .cpp includes the .h. This means that each .o contains a const int x. When the linker links these together, you get multiple definitions.
The solution is to modify the .h
#ifndef STORY
#define STORY
extern const int x; //Do not initialise
#endif
and in a single .cpp:
const int x=4
Edit:
I didnt even see the #include <file.cpp> business. Don't do that. Its horrible.
This should be like :
header.h:
#ifndef STORY
#define STORY
const int x = 4;
void func1();
void func2();
#endif
fun1.cpp
#include <iostream>
#include "header.h"
void func1()
{
int w = x;
std::cout << "func1 " << w << std::endl;
}
fun2.cpp
#include <iostream>
#include "header.h"
void func2()
{
int z = x;
std::cout << "func2 " << z << std::endl;
}
main.cpp
#include <iostream>
#include "header.h"
using namespace std;
int main()
{
func1();
func2();
}
You can not include ".cpp"
It can be done such as :
header.h:
#ifndef STORY
#define STORY
const int x = 4;
void func1();
void func2();
#endif
fun1.cpp
#include <iostream>
#include "header.h"
using namespace std;
void func1()
{
int w = x;
cout << "func1 value of w = " << w << "\n";
}
fun2.cpp
#include <iostream>
#include "header.h"
using namespace std;
void func2()
{
int z = x;
cout << "func2 value of z = " << z << "\n";
}
main.cpp
#include <iostream>
#include "header.h"
int main()
{
func1();
func2();
}
".cpp" file cannot be included in main source file.