Skip to content

Commit 19f5c05

Browse files
committed
Fail at compile time when passing no arguments to a get*Component function that takes arguments instead of default-constructing all arguments.
1 parent ec4a9c6 commit 19f5c05

5 files changed

Lines changed: 198 additions & 9 deletions

File tree

include/fruit/impl/component.defn.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ inline PartialComponent<fruit::impl::InstallComponent<fruit::Component<OtherComp
247247
PartialComponent<Bindings...>::install(fruit::Component<OtherComponentParams...> (*getComponent)(FormalArgs...),
248248
Args&&... args) {
249249
using IntCollector = int[];
250-
(void)IntCollector{0, fruit::impl::checkAcceptableComponentInstallArg<FormalArgs>()...};
250+
(void)IntCollector{0, fruit::impl::checkAcceptableComponentInstallArg<FormalArgs, Args>()...};
251251

252252
using Op = OpFor<fruit::impl::InstallComponent<fruit::Component<OtherComponentParams...>(FormalArgs...)>>;
253253
(void)typename fruit::impl::meta::CheckIfError<Op>::type();
@@ -277,7 +277,7 @@ inline typename PartialComponent<Bindings...>::template PartialComponentWithRepl
277277
PartialComponent<Bindings...>::replace(fruit::Component<OtherComponentParams...> (*getReplacedComponent)(FormalArgs...),
278278
Args&&... args) {
279279
using IntCollector = int[];
280-
(void)IntCollector{0, fruit::impl::checkAcceptableComponentInstallArg<FormalArgs>()...};
280+
(void)IntCollector{0, fruit::impl::checkAcceptableComponentInstallArg<FormalArgs, Args>()...};
281281

282282
std::tuple<FormalArgs...> args_tuple{std::forward<Args>(args)...};
283283

@@ -294,7 +294,7 @@ PartialComponent<Bindings...>::
294294
PartialComponentWithReplacementInProgress<OtherComponent, GetReplacedComponentFormalArgs...>::with(
295295
OtherComponent (*getReplacementComponent)(GetReplacementComponentFormalArgs...), Args&&... args) {
296296
using IntCollector = int[];
297-
(void)IntCollector{0, fruit::impl::checkAcceptableComponentInstallArg<GetReplacementComponentFormalArgs>()...};
297+
(void)IntCollector{0, fruit::impl::checkAcceptableComponentInstallArg<GetReplacementComponentFormalArgs, Args>()...};
298298

299299
std::tuple<GetReplacementComponentFormalArgs...> args_tuple{std::forward<Args>(args)...};
300300

include/fruit/impl/component_function.defn.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ inline ComponentFunction<ComponentType, ComponentFunctionArgs...>::ComponentFunc
2828
ComponentType (*getComponent)(ComponentFunctionArgs...), ComponentFunctionArgs... args)
2929
: getComponent(getComponent), args_tuple{args...} {
3030
using IntCollector = int[];
31-
(void)IntCollector{0, fruit::impl::checkAcceptableComponentInstallArg<ComponentFunctionArgs>()...};
31+
(void)IntCollector{0, fruit::impl::checkAcceptableComponentInstallArg<ComponentFunctionArgs, ComponentFunctionArgs>()...};
3232
}
3333

3434
template <typename ComponentType, typename... ComponentFunctionArgs>

include/fruit/impl/component_install_arg_checks.defn.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,23 @@
2525
namespace fruit {
2626
namespace impl {
2727

28-
template <typename T>
28+
template <typename FormalT, typename ActualT>
2929
FRUIT_ALWAYS_INLINE inline int checkAcceptableComponentInstallArg() {
3030
// This lambda checks that the required operations on T exist.
3131
// Note that the lambda is never actually executed.
32-
auto checkRequirements = [](const T& constRef, T value) {
33-
T x1(constRef);
34-
T x2(std::move(value));
32+
auto checkRequirements = [](const FormalT& constRef, ActualT actual, FormalT value) {
33+
FormalT x1(constRef);
34+
FormalT x2(std::move(value));
3535
x1 = constRef;
3636
x2 = std::move(value);
3737
bool b = (constRef == constRef);
38-
std::size_t h = std::hash<T>()(constRef);
38+
std::size_t h = std::hash<FormalT>()(constRef);
39+
FormalT from_actual(actual);
3940
(void)x1;
4041
(void)x2;
4142
(void)b;
4243
(void)h;
44+
(void)from_actual;
4345
};
4446
(void)checkRequirements;
4547
return 0;

tests/test_injector.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,97 @@ def test_empty_injector(self):
4343
COMMON_DEFINITIONS,
4444
source)
4545

46+
def test_injector_with_parameters(self):
47+
source = '''
48+
fruit::Component<> getComponent(int n) {
49+
(void)n;
50+
return fruit::createComponent();
51+
}
52+
53+
int main() {
54+
fruit::Injector<> injector(getComponent, 5);
55+
}
56+
'''
57+
expect_success(
58+
COMMON_DEFINITIONS,
59+
source)
60+
61+
def test_injector_with_parameters_too_few_passed(self):
62+
source = '''
63+
fruit::Component<> getComponent(int n, bool b) {
64+
(void)n;
65+
(void)b;
66+
return fruit::createComponent();
67+
}
68+
69+
int main() {
70+
fruit::Injector<> injector(getComponent, 5);
71+
}
72+
'''
73+
expect_generic_compile_error(
74+
# Clang
75+
'pack expansion contains parameter packs .FormalArgs. and .Args. that have different lengths \\(2 vs. 1\\)'
76+
# GCC
77+
'|mismatched argument pack lengths while expanding .checkAcceptableComponentInstallArg<FormalArgs, Args>().',
78+
COMMON_DEFINITIONS,
79+
source)
80+
81+
def test_injector_with_parameters_too_many_passed(self):
82+
source = '''
83+
fruit::Component<> getComponent(int n) {
84+
(void)n;
85+
return fruit::createComponent();
86+
}
87+
88+
int main() {
89+
fruit::Injector<> injector(getComponent, 5, true);
90+
}
91+
'''
92+
expect_generic_compile_error(
93+
# Clang
94+
'pack expansion contains parameter packs .FormalArgs. and .Args. that have different lengths \\(1 vs. 2\\)'
95+
# GCC
96+
'|mismatched argument pack lengths while expanding .checkAcceptableComponentInstallArg<FormalArgs, Args>().',
97+
COMMON_DEFINITIONS,
98+
source)
99+
100+
def test_injector_with_parameters_none_passed(self):
101+
source = '''
102+
fruit::Component<> getComponent(int n) {
103+
(void) n;
104+
return fruit::createComponent();
105+
}
106+
107+
int main() {
108+
fruit::Injector<> injector(getComponent);
109+
}
110+
'''
111+
expect_generic_compile_error(
112+
# Clang
113+
'pack expansion contains parameter packs .FormalArgs. and .Args. that have different lengths \\(1 vs. 0\\)'
114+
# GCC
115+
'|mismatched argument pack lengths while expanding .checkAcceptableComponentInstallArg<FormalArgs, Args>().',
116+
COMMON_DEFINITIONS,
117+
source)
118+
119+
def test_injector_without_parameters_but_some_passed(self):
120+
source = '''
121+
fruit::Component<> getComponent() {
122+
return fruit::createComponent();
123+
}
124+
125+
int main() {
126+
fruit::Injector<> injector(getComponent, 1);
127+
}
128+
'''
129+
expect_generic_compile_error(
130+
# Clang
131+
'pack expansion contains parameter packs .FormalArgs. and .Args. that have different lengths \\(0 vs. 1\\)'
132+
# GCC
133+
'|mismatched argument pack lengths while expanding .checkAcceptableComponentInstallArg<FormalArgs, Args>().',
134+
COMMON_DEFINITIONS,
135+
source)
136+
46137
@parameterized.parameters([
47138
'X',
48139
'fruit::Annotated<Annotation1, X>',

tests/test_install.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,102 @@ def test_install_with_args_success(self):
374374
'''
375375
expect_success(COMMON_DEFINITIONS, source)
376376

377+
def test_install_with_args_too_few_passed(self):
378+
source = '''
379+
fruit::Component<> getParentComponent(int n, bool b) {
380+
(void)n;
381+
(void)b;
382+
return fruit::createComponent();
383+
}
384+
385+
fruit::Component<> getComponent() {
386+
return fruit::createComponent()
387+
.install(getParentComponent, 5);
388+
}
389+
390+
int main() {
391+
fruit::Injector<> injector(getComponent);
392+
}
393+
'''
394+
expect_generic_compile_error(
395+
# Clang
396+
'pack expansion contains parameter packs .FormalArgs. and .Args. that have different lengths \\(2 vs. 1\\)'
397+
# GCC
398+
'|mismatched argument pack lengths while expanding .checkAcceptableComponentInstallArg<FormalArgs, Args>().',
399+
COMMON_DEFINITIONS,
400+
source)
401+
402+
def test_install_with_args_too_many_passed(self):
403+
source = '''
404+
fruit::Component<> getParentComponent(int n) {
405+
(void)n;
406+
return fruit::createComponent();
407+
}
408+
409+
fruit::Component<> getComponent() {
410+
return fruit::createComponent()
411+
.install(getParentComponent, 5, true);
412+
}
413+
414+
int main() {
415+
fruit::Injector<> injector(getComponent);
416+
}
417+
'''
418+
expect_generic_compile_error(
419+
# Clang
420+
'pack expansion contains parameter packs .FormalArgs. and .Args. that have different lengths \\(1 vs. 2\\)'
421+
# GCC
422+
'|mismatched argument pack lengths while expanding .checkAcceptableComponentInstallArg<FormalArgs, Args>().',
423+
COMMON_DEFINITIONS,
424+
source)
425+
426+
def test_install_with_args_none_passed(self):
427+
source = '''
428+
fruit::Component<> getParentComponent(int n) {
429+
(void)n;
430+
return fruit::createComponent();
431+
}
432+
433+
fruit::Component<> getComponent() {
434+
return fruit::createComponent()
435+
.install(getParentComponent);
436+
}
437+
438+
int main() {
439+
fruit::Injector<> injector(getComponent);
440+
}
441+
'''
442+
expect_generic_compile_error(
443+
# Clang
444+
'pack expansion contains parameter packs .FormalArgs. and .Args. that have different lengths \\(1 vs. 0\\)'
445+
# GCC
446+
'|mismatched argument pack lengths while expanding .checkAcceptableComponentInstallArg<FormalArgs, Args>().',
447+
COMMON_DEFINITIONS,
448+
source)
449+
450+
def test_install_without_args_but_some_passed(self):
451+
source = '''
452+
fruit::Component<> getParentComponent() {
453+
return fruit::createComponent();
454+
}
455+
456+
fruit::Component<> getComponent() {
457+
return fruit::createComponent()
458+
.install(getParentComponent, 5);
459+
}
460+
461+
int main() {
462+
fruit::Injector<> injector(getComponent);
463+
}
464+
'''
465+
expect_generic_compile_error(
466+
# Clang
467+
'pack expansion contains parameter packs .FormalArgs. and .Args. that have different lengths \\(0 vs. 1\\)'
468+
# GCC
469+
'|mismatched argument pack lengths while expanding .checkAcceptableComponentInstallArg<FormalArgs, Args>().',
470+
COMMON_DEFINITIONS,
471+
source)
472+
377473
def test_install_with_args_error_not_move_constructible(self):
378474
source = '''
379475
struct Arg {

0 commit comments

Comments
 (0)