clang fails to generate defaulted move constructor upon template instantiation
The following code (I couldn't make a shorter MVCE)
unit.h:
#include <vector>
template<typename T>
struct foo
std::vector<T> data;
foo(foo&&) = default; // no assembly generated
foo(std::vector<T>&&v) : data(std::move(v))
;
extern template struct foo<int>; // indicates template instantiation elsewhere
unit.cc:
#include "unit.h"
template struct foo<int>; // forces template intantiation
main.cc:
#include "unit.h"
struct bar
foo<int> f;
bar(foo<int>&&x) : f(std::move(x))
;
bar makeBar(int x)
std::vector<int> v(x);
foo<int> f(std::move(v));
return std::move(f);
int main()
bar x = makeBar(5);
fails to compile under clang (Apple LLVM version 9.0.0 (clang-900.0.39.2) -- which llvm version is that?) with the result:
test> clang++ -std=c++11 -c unit.cc
test> clang++ -std=c++11 -c main.cc
test> clang++ -std=c++11 main.o unit.o
Undefined symbols for architecture x86_64:
"foo<int>::foo(foo<int>&&)", referenced from:
bar::bar(foo<int>&&) in main-476e7b.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Everything works fine with gcc (8.2.0). On inspection, it appears that gcc emits foo<int>::foo(foo<int>&&)
in main.o
, while clang fails to emit it completely.
What is the correct behaviour: should the default
move constructor be emitted with unit.o
or main.o
? Is this a known clang bug?
useful link: https://en.cppreference.com/w/cpp/language/class_template
c++ c++11 templates explicit-instantiation template-instantiation
add a comment |
The following code (I couldn't make a shorter MVCE)
unit.h:
#include <vector>
template<typename T>
struct foo
std::vector<T> data;
foo(foo&&) = default; // no assembly generated
foo(std::vector<T>&&v) : data(std::move(v))
;
extern template struct foo<int>; // indicates template instantiation elsewhere
unit.cc:
#include "unit.h"
template struct foo<int>; // forces template intantiation
main.cc:
#include "unit.h"
struct bar
foo<int> f;
bar(foo<int>&&x) : f(std::move(x))
;
bar makeBar(int x)
std::vector<int> v(x);
foo<int> f(std::move(v));
return std::move(f);
int main()
bar x = makeBar(5);
fails to compile under clang (Apple LLVM version 9.0.0 (clang-900.0.39.2) -- which llvm version is that?) with the result:
test> clang++ -std=c++11 -c unit.cc
test> clang++ -std=c++11 -c main.cc
test> clang++ -std=c++11 main.o unit.o
Undefined symbols for architecture x86_64:
"foo<int>::foo(foo<int>&&)", referenced from:
bar::bar(foo<int>&&) in main-476e7b.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Everything works fine with gcc (8.2.0). On inspection, it appears that gcc emits foo<int>::foo(foo<int>&&)
in main.o
, while clang fails to emit it completely.
What is the correct behaviour: should the default
move constructor be emitted with unit.o
or main.o
? Is this a known clang bug?
useful link: https://en.cppreference.com/w/cpp/language/class_template
c++ c++11 templates explicit-instantiation template-instantiation
Latest XCode is 10, so it's probably LLVM 5 or perhaps 6.
– Matthieu Brucher
Nov 13 '18 at 14:20
add a comment |
The following code (I couldn't make a shorter MVCE)
unit.h:
#include <vector>
template<typename T>
struct foo
std::vector<T> data;
foo(foo&&) = default; // no assembly generated
foo(std::vector<T>&&v) : data(std::move(v))
;
extern template struct foo<int>; // indicates template instantiation elsewhere
unit.cc:
#include "unit.h"
template struct foo<int>; // forces template intantiation
main.cc:
#include "unit.h"
struct bar
foo<int> f;
bar(foo<int>&&x) : f(std::move(x))
;
bar makeBar(int x)
std::vector<int> v(x);
foo<int> f(std::move(v));
return std::move(f);
int main()
bar x = makeBar(5);
fails to compile under clang (Apple LLVM version 9.0.0 (clang-900.0.39.2) -- which llvm version is that?) with the result:
test> clang++ -std=c++11 -c unit.cc
test> clang++ -std=c++11 -c main.cc
test> clang++ -std=c++11 main.o unit.o
Undefined symbols for architecture x86_64:
"foo<int>::foo(foo<int>&&)", referenced from:
bar::bar(foo<int>&&) in main-476e7b.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Everything works fine with gcc (8.2.0). On inspection, it appears that gcc emits foo<int>::foo(foo<int>&&)
in main.o
, while clang fails to emit it completely.
What is the correct behaviour: should the default
move constructor be emitted with unit.o
or main.o
? Is this a known clang bug?
useful link: https://en.cppreference.com/w/cpp/language/class_template
c++ c++11 templates explicit-instantiation template-instantiation
The following code (I couldn't make a shorter MVCE)
unit.h:
#include <vector>
template<typename T>
struct foo
std::vector<T> data;
foo(foo&&) = default; // no assembly generated
foo(std::vector<T>&&v) : data(std::move(v))
;
extern template struct foo<int>; // indicates template instantiation elsewhere
unit.cc:
#include "unit.h"
template struct foo<int>; // forces template intantiation
main.cc:
#include "unit.h"
struct bar
foo<int> f;
bar(foo<int>&&x) : f(std::move(x))
;
bar makeBar(int x)
std::vector<int> v(x);
foo<int> f(std::move(v));
return std::move(f);
int main()
bar x = makeBar(5);
fails to compile under clang (Apple LLVM version 9.0.0 (clang-900.0.39.2) -- which llvm version is that?) with the result:
test> clang++ -std=c++11 -c unit.cc
test> clang++ -std=c++11 -c main.cc
test> clang++ -std=c++11 main.o unit.o
Undefined symbols for architecture x86_64:
"foo<int>::foo(foo<int>&&)", referenced from:
bar::bar(foo<int>&&) in main-476e7b.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Everything works fine with gcc (8.2.0). On inspection, it appears that gcc emits foo<int>::foo(foo<int>&&)
in main.o
, while clang fails to emit it completely.
What is the correct behaviour: should the default
move constructor be emitted with unit.o
or main.o
? Is this a known clang bug?
useful link: https://en.cppreference.com/w/cpp/language/class_template
c++ c++11 templates explicit-instantiation template-instantiation
c++ c++11 templates explicit-instantiation template-instantiation
edited Nov 14 '18 at 6:55
Walter
asked Nov 13 '18 at 14:17
WalterWalter
27.2k1475153
27.2k1475153
Latest XCode is 10, so it's probably LLVM 5 or perhaps 6.
– Matthieu Brucher
Nov 13 '18 at 14:20
add a comment |
Latest XCode is 10, so it's probably LLVM 5 or perhaps 6.
– Matthieu Brucher
Nov 13 '18 at 14:20
Latest XCode is 10, so it's probably LLVM 5 or perhaps 6.
– Matthieu Brucher
Nov 13 '18 at 14:20
Latest XCode is 10, so it's probably LLVM 5 or perhaps 6.
– Matthieu Brucher
Nov 13 '18 at 14:20
add a comment |
1 Answer
1
active
oldest
votes
This is a clang bug. Your code is well formed so whatever would be the strategy of the compiler considering the "as if" rule, your code should compile.
Explicit instantiation of a class template only instantiates members for which a definition is provided [temp.explicit]/9:
An explicit instantiation definition that names a class template specialization explicitly instantiates the class template specialization and is an explicit instantiation definition of only those members that have been defined at the point of instantiation.
Special member function defaulted on their first declaration are only defined when odr-used. So I suppose that the bug is that Clang expects that at the point of explicit instantiation, the defaulted constructor was also instantiated.
So the work around could be first, to declare the move constructor in the header file, then to define it as defaulted in the implementation file:
unit.hpp:
template<typename T>
struct foo
std::vector<T> data;
foo(foo&&)=default;
foo(std::vector<T>&&v) : data(std::move(v))
;
template<T>
foo<T>::foo(foo&&) noexcept;
extern template struct foo<int>;
unit.cpp:
#include <unit.hpp>
template<T>
foo<T>::foo(foo&&) noexcept = default;
template struct foo<int>; //foo(foo&&) has a definition so it is instantiated with the class.
This will force the generation of the definition of the defaulted move constructor (see [dlc.fct.def.default]/5). The draw back is that the definition of foo(foo&&)
is not inline anymore.
Alternatively the solution bellow will work:
template<typename T>
struct foo
std::vector<T> data;
foo(foo&& o)noexcept:datamove(o.data);
foo(std::vector<T>&&v) : data(std::move(v))
;
It is indeed a clang bug. I filed a report and learned that it was already fixed in version 6 (apparently w/o a bug report). As to where the explicitly defaulted members are to be exported, I'm still not 100% sure.
– Walter
Nov 14 '18 at 6:44
Your workaround works (good), but only provides the (otherwise missing) constructor for the particular instantiation, i.e. forfoo<int>
. The code will fail to compile with any otherfoo<T>
(very bad). There appears to be no work around, other than avoiding to mix explicitly defaulted member functions with explicit template instantiation.
– Walter
Nov 14 '18 at 6:54
@Walter OK I know how to fix that too.
– Oliv
Nov 14 '18 at 6:55
@Walter ... Indeed it work with gcc but not with clang! That is an other bug of clang!
– Oliv
Nov 14 '18 at 7:04
Are you sure? Why do you think clang is wrong?
– Walter
Nov 14 '18 at 7:07
|
show 7 more comments
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53283030%2fclang-fails-to-generate-defaulted-move-constructor-upon-template-instantiation%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
This is a clang bug. Your code is well formed so whatever would be the strategy of the compiler considering the "as if" rule, your code should compile.
Explicit instantiation of a class template only instantiates members for which a definition is provided [temp.explicit]/9:
An explicit instantiation definition that names a class template specialization explicitly instantiates the class template specialization and is an explicit instantiation definition of only those members that have been defined at the point of instantiation.
Special member function defaulted on their first declaration are only defined when odr-used. So I suppose that the bug is that Clang expects that at the point of explicit instantiation, the defaulted constructor was also instantiated.
So the work around could be first, to declare the move constructor in the header file, then to define it as defaulted in the implementation file:
unit.hpp:
template<typename T>
struct foo
std::vector<T> data;
foo(foo&&)=default;
foo(std::vector<T>&&v) : data(std::move(v))
;
template<T>
foo<T>::foo(foo&&) noexcept;
extern template struct foo<int>;
unit.cpp:
#include <unit.hpp>
template<T>
foo<T>::foo(foo&&) noexcept = default;
template struct foo<int>; //foo(foo&&) has a definition so it is instantiated with the class.
This will force the generation of the definition of the defaulted move constructor (see [dlc.fct.def.default]/5). The draw back is that the definition of foo(foo&&)
is not inline anymore.
Alternatively the solution bellow will work:
template<typename T>
struct foo
std::vector<T> data;
foo(foo&& o)noexcept:datamove(o.data);
foo(std::vector<T>&&v) : data(std::move(v))
;
It is indeed a clang bug. I filed a report and learned that it was already fixed in version 6 (apparently w/o a bug report). As to where the explicitly defaulted members are to be exported, I'm still not 100% sure.
– Walter
Nov 14 '18 at 6:44
Your workaround works (good), but only provides the (otherwise missing) constructor for the particular instantiation, i.e. forfoo<int>
. The code will fail to compile with any otherfoo<T>
(very bad). There appears to be no work around, other than avoiding to mix explicitly defaulted member functions with explicit template instantiation.
– Walter
Nov 14 '18 at 6:54
@Walter OK I know how to fix that too.
– Oliv
Nov 14 '18 at 6:55
@Walter ... Indeed it work with gcc but not with clang! That is an other bug of clang!
– Oliv
Nov 14 '18 at 7:04
Are you sure? Why do you think clang is wrong?
– Walter
Nov 14 '18 at 7:07
|
show 7 more comments
This is a clang bug. Your code is well formed so whatever would be the strategy of the compiler considering the "as if" rule, your code should compile.
Explicit instantiation of a class template only instantiates members for which a definition is provided [temp.explicit]/9:
An explicit instantiation definition that names a class template specialization explicitly instantiates the class template specialization and is an explicit instantiation definition of only those members that have been defined at the point of instantiation.
Special member function defaulted on their first declaration are only defined when odr-used. So I suppose that the bug is that Clang expects that at the point of explicit instantiation, the defaulted constructor was also instantiated.
So the work around could be first, to declare the move constructor in the header file, then to define it as defaulted in the implementation file:
unit.hpp:
template<typename T>
struct foo
std::vector<T> data;
foo(foo&&)=default;
foo(std::vector<T>&&v) : data(std::move(v))
;
template<T>
foo<T>::foo(foo&&) noexcept;
extern template struct foo<int>;
unit.cpp:
#include <unit.hpp>
template<T>
foo<T>::foo(foo&&) noexcept = default;
template struct foo<int>; //foo(foo&&) has a definition so it is instantiated with the class.
This will force the generation of the definition of the defaulted move constructor (see [dlc.fct.def.default]/5). The draw back is that the definition of foo(foo&&)
is not inline anymore.
Alternatively the solution bellow will work:
template<typename T>
struct foo
std::vector<T> data;
foo(foo&& o)noexcept:datamove(o.data);
foo(std::vector<T>&&v) : data(std::move(v))
;
It is indeed a clang bug. I filed a report and learned that it was already fixed in version 6 (apparently w/o a bug report). As to where the explicitly defaulted members are to be exported, I'm still not 100% sure.
– Walter
Nov 14 '18 at 6:44
Your workaround works (good), but only provides the (otherwise missing) constructor for the particular instantiation, i.e. forfoo<int>
. The code will fail to compile with any otherfoo<T>
(very bad). There appears to be no work around, other than avoiding to mix explicitly defaulted member functions with explicit template instantiation.
– Walter
Nov 14 '18 at 6:54
@Walter OK I know how to fix that too.
– Oliv
Nov 14 '18 at 6:55
@Walter ... Indeed it work with gcc but not with clang! That is an other bug of clang!
– Oliv
Nov 14 '18 at 7:04
Are you sure? Why do you think clang is wrong?
– Walter
Nov 14 '18 at 7:07
|
show 7 more comments
This is a clang bug. Your code is well formed so whatever would be the strategy of the compiler considering the "as if" rule, your code should compile.
Explicit instantiation of a class template only instantiates members for which a definition is provided [temp.explicit]/9:
An explicit instantiation definition that names a class template specialization explicitly instantiates the class template specialization and is an explicit instantiation definition of only those members that have been defined at the point of instantiation.
Special member function defaulted on their first declaration are only defined when odr-used. So I suppose that the bug is that Clang expects that at the point of explicit instantiation, the defaulted constructor was also instantiated.
So the work around could be first, to declare the move constructor in the header file, then to define it as defaulted in the implementation file:
unit.hpp:
template<typename T>
struct foo
std::vector<T> data;
foo(foo&&)=default;
foo(std::vector<T>&&v) : data(std::move(v))
;
template<T>
foo<T>::foo(foo&&) noexcept;
extern template struct foo<int>;
unit.cpp:
#include <unit.hpp>
template<T>
foo<T>::foo(foo&&) noexcept = default;
template struct foo<int>; //foo(foo&&) has a definition so it is instantiated with the class.
This will force the generation of the definition of the defaulted move constructor (see [dlc.fct.def.default]/5). The draw back is that the definition of foo(foo&&)
is not inline anymore.
Alternatively the solution bellow will work:
template<typename T>
struct foo
std::vector<T> data;
foo(foo&& o)noexcept:datamove(o.data);
foo(std::vector<T>&&v) : data(std::move(v))
;
This is a clang bug. Your code is well formed so whatever would be the strategy of the compiler considering the "as if" rule, your code should compile.
Explicit instantiation of a class template only instantiates members for which a definition is provided [temp.explicit]/9:
An explicit instantiation definition that names a class template specialization explicitly instantiates the class template specialization and is an explicit instantiation definition of only those members that have been defined at the point of instantiation.
Special member function defaulted on their first declaration are only defined when odr-used. So I suppose that the bug is that Clang expects that at the point of explicit instantiation, the defaulted constructor was also instantiated.
So the work around could be first, to declare the move constructor in the header file, then to define it as defaulted in the implementation file:
unit.hpp:
template<typename T>
struct foo
std::vector<T> data;
foo(foo&&)=default;
foo(std::vector<T>&&v) : data(std::move(v))
;
template<T>
foo<T>::foo(foo&&) noexcept;
extern template struct foo<int>;
unit.cpp:
#include <unit.hpp>
template<T>
foo<T>::foo(foo&&) noexcept = default;
template struct foo<int>; //foo(foo&&) has a definition so it is instantiated with the class.
This will force the generation of the definition of the defaulted move constructor (see [dlc.fct.def.default]/5). The draw back is that the definition of foo(foo&&)
is not inline anymore.
Alternatively the solution bellow will work:
template<typename T>
struct foo
std::vector<T> data;
foo(foo&& o)noexcept:datamove(o.data);
foo(std::vector<T>&&v) : data(std::move(v))
;
edited Nov 14 '18 at 10:36
answered Nov 13 '18 at 15:48
OlivOliv
8,5341956
8,5341956
It is indeed a clang bug. I filed a report and learned that it was already fixed in version 6 (apparently w/o a bug report). As to where the explicitly defaulted members are to be exported, I'm still not 100% sure.
– Walter
Nov 14 '18 at 6:44
Your workaround works (good), but only provides the (otherwise missing) constructor for the particular instantiation, i.e. forfoo<int>
. The code will fail to compile with any otherfoo<T>
(very bad). There appears to be no work around, other than avoiding to mix explicitly defaulted member functions with explicit template instantiation.
– Walter
Nov 14 '18 at 6:54
@Walter OK I know how to fix that too.
– Oliv
Nov 14 '18 at 6:55
@Walter ... Indeed it work with gcc but not with clang! That is an other bug of clang!
– Oliv
Nov 14 '18 at 7:04
Are you sure? Why do you think clang is wrong?
– Walter
Nov 14 '18 at 7:07
|
show 7 more comments
It is indeed a clang bug. I filed a report and learned that it was already fixed in version 6 (apparently w/o a bug report). As to where the explicitly defaulted members are to be exported, I'm still not 100% sure.
– Walter
Nov 14 '18 at 6:44
Your workaround works (good), but only provides the (otherwise missing) constructor for the particular instantiation, i.e. forfoo<int>
. The code will fail to compile with any otherfoo<T>
(very bad). There appears to be no work around, other than avoiding to mix explicitly defaulted member functions with explicit template instantiation.
– Walter
Nov 14 '18 at 6:54
@Walter OK I know how to fix that too.
– Oliv
Nov 14 '18 at 6:55
@Walter ... Indeed it work with gcc but not with clang! That is an other bug of clang!
– Oliv
Nov 14 '18 at 7:04
Are you sure? Why do you think clang is wrong?
– Walter
Nov 14 '18 at 7:07
It is indeed a clang bug. I filed a report and learned that it was already fixed in version 6 (apparently w/o a bug report). As to where the explicitly defaulted members are to be exported, I'm still not 100% sure.
– Walter
Nov 14 '18 at 6:44
It is indeed a clang bug. I filed a report and learned that it was already fixed in version 6 (apparently w/o a bug report). As to where the explicitly defaulted members are to be exported, I'm still not 100% sure.
– Walter
Nov 14 '18 at 6:44
Your workaround works (good), but only provides the (otherwise missing) constructor for the particular instantiation, i.e. for
foo<int>
. The code will fail to compile with any other foo<T>
(very bad). There appears to be no work around, other than avoiding to mix explicitly defaulted member functions with explicit template instantiation.– Walter
Nov 14 '18 at 6:54
Your workaround works (good), but only provides the (otherwise missing) constructor for the particular instantiation, i.e. for
foo<int>
. The code will fail to compile with any other foo<T>
(very bad). There appears to be no work around, other than avoiding to mix explicitly defaulted member functions with explicit template instantiation.– Walter
Nov 14 '18 at 6:54
@Walter OK I know how to fix that too.
– Oliv
Nov 14 '18 at 6:55
@Walter OK I know how to fix that too.
– Oliv
Nov 14 '18 at 6:55
@Walter ... Indeed it work with gcc but not with clang! That is an other bug of clang!
– Oliv
Nov 14 '18 at 7:04
@Walter ... Indeed it work with gcc but not with clang! That is an other bug of clang!
– Oliv
Nov 14 '18 at 7:04
Are you sure? Why do you think clang is wrong?
– Walter
Nov 14 '18 at 7:07
Are you sure? Why do you think clang is wrong?
– Walter
Nov 14 '18 at 7:07
|
show 7 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53283030%2fclang-fails-to-generate-defaulted-move-constructor-upon-template-instantiation%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Latest XCode is 10, so it's probably LLVM 5 or perhaps 6.
– Matthieu Brucher
Nov 13 '18 at 14:20