Implement compile-time function type verification
Company: OpenAI
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Technical Screen
Quick Answer: This question evaluates proficiency in C++ template metaprogramming, type traits, SFINAE and Concepts for compile-time callable signature verification, including handling of noexcept, cv/ref qualifiers and implicit conversions.
Part 1: Traits-Style Callable Signature Match
Constraints
- 0 <= len(signatures) <= 10000
- Each signature string has length between 4 and 100
- All signatures are well-formed
- Types only come from: bool, int, long, double, string, void
- Only the listed implicit conversions are allowed
Examples
Input: (['(int)->long'], '(int)->double')
Expected Output: True
Explanation: The argument type matches exactly, and the candidate return type long can be widened to double.
Input: (['(double)->int', '(string)->int'], '(int)->long')
Expected Output: True
Explanation: The first overload matches because int can convert to double and int can convert to long on return.
Input: (['()->void'], '()->void')
Expected Output: True
Explanation: Edge case: zero-argument callable with void return matches exactly.
Input: (['(long)->int'], '(double)->int')
Expected Output: False
Explanation: double cannot implicitly convert to long under the allowed rules, so the call is not valid.
Input: ([], '(int)->int')
Expected Output: False
Explanation: Edge case: with no candidate overloads, nothing can match.
Solution
def solution(signatures, target_signature):
rank = {'bool': 0, 'int': 1, 'long': 2, 'double': 3}
def convertible(src, dst):
if src == dst:
return True
if src == 'void' or dst == 'void':
return False
if src in rank and dst in rank:
return rank[src] <= rank[dst]
return False
def parse(sig):
sig = ''.join(sig.split())
left, ret = sig.split('->')
args_text = left[1:-1]
args = [] if args_text == '' else args_text.split(',')
return args, ret
target_args, target_ret = parse(target_signature)
for sig in signatures:
params, ret = parse(sig)
if len(params) != len(target_args):
continue
args_ok = all(convertible(arg, param) for arg, param in zip(target_args, params))
ret_ok = convertible(ret, target_ret)
if args_ok and ret_ok:
return True
return False
Time complexity: O(N * L), where N is the number of candidate signatures and L is the average signature length. Space complexity: O(L) excluding the input storage.
Hints
- Parse each signature into an argument list and a return type. Overloads are just multiple candidate strings.
- Model implicit conversion as a directed rule set or numeric rank order, then test every parameter position plus the return type.
Part 2: Concepts-Style Callable Signature Match with Qualifiers
Constraints
- 0 <= len(signatures) <= 10000
- Each signature string has length between 6 and 120
- All signatures are well-formed
- Types only come from: bool, int, long, double, string, void
- Qualifiers only come from: const, &, &&, noexcept
- A signature contains at most one of '&' and '&&'
- Only the listed implicit conversions are allowed
Examples
Input: (['[const,&](int)->long', '[&&](int)->double'], '[const,&](int)->double')
Expected Output: True
Explanation: The first overload satisfies both required qualifiers, accepts int, and returns long which can convert to double.
Input: (['[&](int)->double'], '[&,noexcept](int)->double')
Expected Output: False
Explanation: The candidate is missing the required noexcept qualifier.
Input: (['[&&](int)->int'], '[&](int)->int')
Expected Output: False
Explanation: A target requiring '&' does not match a candidate marked only with '&&'.
Input: (['[noexcept](double)->int', '[](string)->int'], '[noexcept](int)->long')
Expected Output: True
Explanation: The first overload matches: int can convert to double, int can convert to long on return, and noexcept is present.
Input: (['[const,noexcept]()->void'], '[noexcept]()->void')
Expected Output: True
Explanation: Edge case: zero-argument callable. The target only requires noexcept, so the extra const is allowed.
Solution
def solution(signatures, target_signature):
rank = {'bool': 0, 'int': 1, 'long': 2, 'double': 3}
def convertible(src, dst):
if src == dst:
return True
if src == 'void' or dst == 'void':
return False
if src in rank and dst in rank:
return rank[src] <= rank[dst]
return False
def parse(sig):
sig = ''.join(sig.split())
end = sig.find(']')
quals_text = sig[1:end]
rest = sig[end + 1:]
left, ret = rest.split('->')
args_text = left[1:-1]
args = [] if args_text == '' else args_text.split(',')
quals = set(filter(None, quals_text.split(',')))
return quals, args, ret
def qualifiers_match(candidate, target):
for q in ('const', 'noexcept'):
if q in target and q not in candidate:
return False
if '&' in target and '&' not in candidate:
return False
if '&&' in target and '&&' not in candidate:
return False
return True
target_quals, target_args, target_ret = parse(target_signature)
for sig in signatures:
quals, params, ret = parse(sig)
if len(params) != len(target_args):
continue
if not qualifiers_match(quals, target_quals):
continue
args_ok = all(convertible(arg, param) for arg, param in zip(target_args, params))
ret_ok = convertible(ret, target_ret)
if args_ok and ret_ok:
return True
return False
Time complexity: O(N * L), where N is the number of candidate signatures and L is the average signature length. Space complexity: O(L) excluding the input storage.
Hints
- Treat the bracketed qualifier list as a set of properties. The target's qualifiers are requirements; the candidate may have extra ones.
- After qualifier matching, reuse the same argument-conversion and return-conversion logic from Part 1.