MSVC compilation failure on explicit variadic template function instantiation - c++

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.

Related

Explicit instantiation unexpected behavior - breaking ODR does not cause compilation errors

Let's consider the following code:
output_data.hpp
#pragma once
#include <functional>
#include <cstddef>
namespace example {
using Callback = std::function<void(int)>;
struct OutputData {
void * data = nullptr;
std::size_t size = 5;
Callback callback = nullptr;
};
} // namespace example
buffer.hpp
#pragma once
#include <vector>
namespace example {
template <typename T>
struct Buffer {
Buffer(std::size_t size);
std::vector<T> buffer_;
};
} // namespace example
buffer.cpp
#include "buffer.hpp"
#include "output_data.hpp"
namespace example {
template <typename T>
Buffer<T>::Buffer(std::size_t size) : buffer_(size)
{
}
template struct Buffer<OutputData>;
} // namespace example
main.cpp
#include <iostream>
#include <memory>
#include <functional>
#include "buffer.hpp"
namespace example {
using Callback = std::function<void(int)>;
struct OutputData {
Callback callback = nullptr;
};
}
int main() {
std::shared_ptr<int> ptr = std::make_shared<int>(5);
example::Buffer<example::OutputData> buf(3);
example::Callback simple_callback = [=](int a) {
std::cout << a << *ptr << "\n";
};
buf.buffer_[1].callback = simple_callback;
}
[Godbolt][https://godbolt.org/z/EEzE7oerb]
The compilation ends without errors, while the execution ends with Segmentation Fault. Segfault is caused by the fact that in main.cpp I used a different struct than the one in buffer.cpp during explicit instantiation. Why was the code compiled without errors?
Is it possible to detect such programming mistakes during compilation? Is there any warning flag for detecting such cases ?
Why was the code compiled without errors?
Credit to Richard Critten's comment for the source.
"The compiler is not required to diagnose this violation, but the behavior of the program that violates it is undefined"
Is it possible to detect such programming mistakes during compilation?
Yes, see below.
Is there any warning flag for detecting such cases ?
Your ODR violation does not exist during compilation of any single source file. It only exists during (after) linking.
g++ will, unless disabled, warn of ODR violations during link time if Link Time Optimization is enabled with the flag -flto
[Godbolt]

What would be the reason that the explicit instantiation failed?

/* MyClass.h */
class MyClass
{
public:
template <typename T>
void Foo(const T &val);
};
/* MyClass.cpp */
#include <string>
#include <iostream>
#define EXPLICIT_INSTANTIATION_FOO(MyType) \
template void MyClass::Foo(const MyType &val)
EXPLICIT_INSTANTIATION_FOO(int);
EXPLICIT_INSTANTIATION_FOO(float);
EXPLICIT_INSTANTIATION_FOO(std::string);
template <typename T>
void MyClass::Foo(const T &val)
{
std::cout << "My Value: " << val << std::endl;
}
For some other reason, I couldn't define the Foo in the header file, then I try to explicitly instantiate all the types I need in the source file. And it works pretty well on g++-4.8.
Then once it I submit the codes, I find the building failed on Windows 10, x86_64, cl19. It was an unresolved symbol error there and was look like my instantiations weren't be exposed from the object.
But interestingly, when I try to reproduce this error on my own Windows by making that simple snippet above. I found it works well on my vs2017 with cl19...
There would be thousands of configuration differs between the company and the personal machine, but I just don't have any idea that what would be the reason why the instantiations don't work there... Any ideas appreciated! :)
SUPPLEMENTS:
FYI, the complete codes are here:
/* MyClass.h */
#pragma once
class MyClass
{
public:
MyClass();
~MyClass();
template <typename T>
void Foo(const T &val);
};
/* MyClass.cpp */
#include "MyClass.h"
#include <iostream>
#include <string>
#define EXPLICIT_INSTANTIATION_FOO(MyType) \
template void MyClass::Foo(const MyType &val)
EXPLICIT_INSTANTIATION_FOO(int);
EXPLICIT_INSTANTIATION_FOO(float);
EXPLICIT_INSTANTIATION_FOO(std::string);
MyClass::MyClass() {}
MyClass::~MyClass() {}
template <typename T>
void MyClass::Foo(const T &val)
{
std::cout << "My Value: " << val << std::endl;
}
/* main.cpp */
#include "MyClass.h"
#include <iostream>
#include <string>
int main()
{
std::string v = "hello";
MyClass cls;
cls.Foo(v);
std::cin.get();
return 0;
}
Which passed the compilation on msvc.
Move the function definition to before the EXPLICIT_INSTANTIATION_FOO invocations. This is something that never should have worked but compilersllowed, they are slowly coming around on it. If you want to make msvc reject it use the /permissive- flag.
Thanks, everybody, this silly question is solved.
The reason is the template instantiations will not inherit the dllexport attribute from the template definition on Windows. And obviously, it works on my silly test codes because it wasn't compiled as the DLL :)

Incomplete declaration of a partially specialized template

I am trying to partially specialize the std::hash struct for my own class TestHandle, and this class has its implementation split up using the opaque pointer idiom. So I am trying to provide the impl class with its own std::hash specialization. But I am running into templating problems.
Could someone help me understand why this is happening? I have attached all the necessary code below.
TestHandle.h
#pragma once
#include <memory>
class TestHandle {
public:
TestHandle();
void print();
class Impl;
std::unique_ptr<Impl> implementation;
};
TestHandle.cpp
#include "TestHandle.h"
#include "Impl.h"
#include <iostream>
using std::cout;
using std::endl;
TestHandle::TestHandle() : implementation{new TestHandle::Impl} { }
void TestHandle::print() {
this->implementation->print();
cout << "Hash of this->implementation is "
<< std::hash<TestHandle::Impl>()(*this->implementation) << endl;
}
Impl.h
#pragma once
#include "TestHandle.h"
#include <functional>
class TestHandle::Impl {
public:
void print();
int inner_integer;
};
namespace std {
template <> struct std::hash<TestHandle::Impl>;
}
Impl.cpp
#include "TestHandle.h"
#include "Impl.h"
#include <iostream>
using std::cout;
using std::endl;
#include <functional>
namespace std {
template <> struct hash <TestHandle::Impl> {
size_t operator() (const TestHandle::Impl& implementation) {
return std::hash<int>()(implementation.inner_integer);
}
};
}
void TestHandle::Impl::print() {
cout << "Printing from impl" << endl;
}
I am compiling with the following command
g++ -std=c++14 -c Impl.cpp TestHandle.cpp
and am getting the following error
TestHandle.cpp:11:12: error: invalid use of incomplete type 'std::hash<TestHandle::Impl>'
<< std::hash<TestHandle::Impl>()(*this->implementation) << endl;
template <> struct std::hash<TestHandle::Impl>;
Just forward declares the specialisation. It doesn't have to implement all the method (or any) of the original template. The compiler has no idea about the operator().
You will need to define the struct (in place of just the declaration);
template <> struct hash <TestHandle::Impl> {
size_t operator() (const TestHandle::Impl& implementation) const noexcept;
};
Side note: you will also need to provide the primary template (via inclusion) of <functional> (missing in the original listed code).

The strange behaviour when using c++ specialization template

I am newbie in programming with c++ template. I have 3 code files
main.cpp
#include "template_test.h"
#include <iostream>
using namespace std;
int main()
{
mytest<int> mt;
mt.method(1);
system("pause");
return 0;
}
template_test.h
#include <iostream>
using namespace std;
template<class T>
class mytest
{
public:
void method(T input){}
};
template<>
void mytest<int>::method(int input)
{
cout << "ok" << endl;
}
template_test.cpp
#include "template_test.h"
//empty
The code takes effective in VS2013. However when I change my code to 2 sitiuations, there are both something wrong with my code.
1.The first is with the linker error code.
main.cpp
#include "template_test.h"
#include <iostream>
using namespace std;
int main()
{
mytest<int> mt;
mt.method(1);
system("pause");
return 0;
}
template_test.h
#include <iostream>
using namespace std;
template<class T>
class mytest
{
public:
void method(T input);
};
template<class T>
void mytest<T>::method(T input)
{
cout << " " << endl;
}//explicit specialization here
template<>
void mytest<int>::method(int input)
{
cout << "ok" << endl;
}
template_test.cpp
#include "template_test.h"
//empty
2.The second output nothing instead of the correct answer 'ok'.
main.cpp
#include "template_test.h"
#include <iostream>
using namespace std;
int main()
{
mytest<int> mt;
mt.method(1);
system("pause");
return 0;
}
template_test.h
#include <iostream>
using namespace std;
template<class T>
class mytest
{
public:
void method(T input){}
};
template_test.cpp
#include "template_test.h"
template<>
void mytest<int>::method(int input)
{
cout << "ok" << endl;
}//move from .h to .cpp file here
The strange behaviour of c++ template makes me confused. So, what's the problem?
The first problem is caused by the fact that your explicit specialization
template<>
void mytest<int>::method(int input)
{
cout << "ok" << endl;
}
is defined in the header file and outside the class definition and without the keyword inline.
An explicit specialization causes an actual function (rather than a mere template) to be defined. That definition will occur in every translation unit, so if you compile template_test.cpp and main.cpp separately, the definition of the function will be included in both object files, causing a multiple-definition error at linking time (because it's a violation of the ODR, the one-definition-rule).
You best avoid this either by including the function definition inside the class template definition (by specializing the entire class template for int), or using the keyword inline:
template<>
inline void mytest<int>::method(int input)
{
cout << "ok" << endl;
}
The second problem is caused by the fact that a template specialization must always be declared before it is used:
(14.7.3/6) If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required. An implicit instantiation is never generated for an explicit specialization that is declared but not defined. [...]
Since your main.cpp includes the header file, but not the .cpp file, the declaration of the template specialization is not known when it is used in main.cpp. You solve this best by including the specialization in the header file, either inside the class template definition (by specializing the entire class template for int), or using the keyword inline outside the class template definition.
You second question first. Remember a rule when using template. Do not separate the template implementation to a cpp file. Make them all in one .h file.
You first code compiles fine in my VC2012, I am not sure what's wrong with VC2013.

Simple function not working when using namespaces

I have the below code that compiles and executes without error, but the line that should be printed in the menu() function is never printed.
Menu.cpp
#include "stdio.h"
#include "Menu.hpp"
#include <iostream>
using namespace std;
namespace View
{
void Menu::startMenu()
{
cout << "2\n";
}
}
Menu.hpp
#ifndef MENU_H //"Header guard"
#define MENU_H
namespace View
{
class Menu
{
void startMenu();
};
}
#endif
I wrote a simple test to call the menu function, if it works correctly the output should be
1
2
3
but the 2 is never printed.
MenuTest.cpp
#include "Menu.hpp"
#include "stdio.h"
#include <iostream>
using namespace std;
int main()
{
cout << "1\n";
View::Menu startMenu();
cout << "3\n";
}
Can someone see what's going on here?
View::Menu startMenu();
Declares a function which returns View::Menu type, which is also known as most vexing parse
To initialize an object and call it's member function, you should do:
View::Menu menu;
menu.startMenu();
BTW, you need to make startMenu() function public:
class Menu
{
public: //<-----
void startMenu();
};
See live sample.
help this helps.
Because you declare function "startMenu()", that is returns type "View:Menu"
But you don't call function startMenu().
Try make following code:
View::Menu obj;
obj.startMenu();
PS. And make startMenu() as public:
class Menu
{
public:
void startMenu();
};
When substracting the brckets of View::Menu startMenu();, the code
View::Menu startMenu;
is a object definition of View::Menu, which does not call the function Menu::startMenu(). And that is when "cout << "2\n";" is not executed. To call further Menu::startMenu():
startMenu.startMenu();