Compile-time keypath safety macro ver.2 for Objective-C

The Compile-time keypath safety macro did not prevent the property name contamination.

Here is a new method successfully prevents the contamination by using @encode() and typeof() together instead of @selector().

How it works:

// Activate compile-time keypath check with @encode() and typeof()
// Single level keypath
[self valueForKey:((void)@encode(typeof(((typeof(self))nil).param_)), @"param_")];

// Multi level keypath
[self valueForKeyPath:((void)@encode(typeof(((typeof(self))nil).items_.firstObject)), @"items_.firstObject")];

The advantage of new method is that properties contained in a class can be strictly tracked. On the other hand, there are some disadvantages: the class name must be specified every time, and if the keypath contains an abstract class such as 'id' in the middle of the keypath, subsequent keypaths cannot be recognized.

So, in addition to the new method, the old method using @selector() should be used as a weak style version starting with 'weak_'.

And to improve convenience for specifying self-class properties, a version starting with 'self_' can be created in which the class name should be omitted.

Implementation:

// General use pseudo-recursible macro algorithm (maximum 16 level)
#define VA_ARGS_CONCAT_IMPL(x, y)\
    x##y
#define VA_ARGS_CONCAT(x, y)\
    VA_ARGS_CONCAT_IMPL(x, y)
#define VA_ARGS_COUNT_LOOKUP()\
    16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#define VA_ARGS_COUNT_INDEX(a1, a2, a3, a4, a5, a6, a7, a8,\
    a9, a10, a11, a12, a13, a14, a15, a16, n, ...)\
    n
#define VA_ARGS_COUNT_IMPL(...)\
    VA_ARGS_COUNT_INDEX(__VA_ARGS__)
#define VA_ARGS_COUNT(...)\
    VA_ARGS_COUNT_IMPL(__VA_ARGS__, VA_ARGS_COUNT_LOOKUP())
#define VA_ARGS_FOR_1(f, g, a)\
    f(a)
#define VA_ARGS_FOR_2(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_1(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_3(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_2(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_4(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_3(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_5(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_4(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_6(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_5(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_7(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_6(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_8(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_7(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_9(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_8(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_10(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_9(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_11(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_10(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_12(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_11(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_13(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_12(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_14(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_13(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_15(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_14(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_16(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_15(f, g, __VA_ARGS__))
#define VA_ARGS_FOR(...)\
    VA_ARGS_CONCAT(VA_ARGS_FOR_, VA_ARGS_COUNT(__VA_ARGS__))

// Utility macros
#define VA_ARGS_GLUE_DOT(x, y)\
    x.y
#define VA_ARGS_GLUE_COMMA(x, y)\
    x, y

// KeyPath safety macro algorithm (Core)
#define KEYOF_LOGIC_ECHO(param)\
    param
#define KEYOF_LOGIC_PROPERTY(param)\
    (void)@encode(typeof(param))
#define KEYOF_LOGIC_SELECTOR(param)\
    (void)@selector(param)
#define KEYOF_LOGIC_LITERAL_IMPL(param)\
    @#param
#define KEYOF_LOGIC_LITERAL(param)\
    KEYOF_LOGIC_LITERAL_IMPL(param)
#define keyof(type, property)\
    (KEYOF_LOGIC_PROPERTY(((type)nil).property), KEYOF_LOGIC_LITERAL(property))
#define self_keyof(property)\
    (keyof(typeof(self), property))
#define weak_keyof(property)\
    (KEYOF_LOGIC_SELECTOR(property), KEYOF_LOGIC_LITERAL(property))
#define keypathof_impl(prefix, suffix, type, ...)\
    (KEYOF_LOGIC_PROPERTY(((type)nil).VA_ARGS_FOR(__VA_ARGS__)\
        (KEYOF_LOGIC_ECHO, VA_ARGS_GLUE_DOT, __VA_ARGS__)),\
    KEYOF_LOGIC_LITERAL(prefix)\
    KEYOF_LOGIC_LITERAL(VA_ARGS_FOR(__VA_ARGS__)\
        (KEYOF_LOGIC_ECHO, VA_ARGS_GLUE_DOT, __VA_ARGS__))\
    KEYOF_LOGIC_LITERAL(suffix))
#define keypathof(type, ...)\
    (keypathof_impl(, , type, __VA_ARGS__))
#define self_keypathof(...)\
    (keypathof(typeof(self), __VA_ARGS__))
#define weak_keypathof_impl(prefix, suffix, ...)\
    (KEYOF_LOGIC_ECHO(VA_ARGS_FOR(__VA_ARGS__)\
        (KEYOF_LOGIC_SELECTOR, VA_ARGS_GLUE_COMMA, __VA_ARGS__)),\
    KEYOF_LOGIC_LITERAL(prefix)\
    KEYOF_LOGIC_LITERAL(VA_ARGS_FOR(__VA_ARGS__)\
        (KEYOF_LOGIC_ECHO, VA_ARGS_GLUE_DOT, __VA_ARGS__))\
    KEYOF_LOGIC_LITERAL(suffix))
#define weak_keypathof(...)\
    (weak_keypathof_impl(, , __VA_ARGS__))

// KeyPath safety macro algorithm (Extended)
#define keypath_count_of(type, ...)\
    (keypathof_impl(, .@count, type, __VA_ARGS__))
#define avg_keypath_of(type, ...)\
    (keypathof_impl(@avg., , type, __VA_ARGS__))
#define max_keypath_of(type, ...)\
    (keypathof_impl(@max., , type, __VA_ARGS__))
#define min_keypath_of(type, ...)\
    (keypathof_impl(@min., , type, __VA_ARGS__))
#define sum_keypath_of(type, ...)\
    (keypathof_impl(@sum., , type, __VA_ARGS__))
#define unionOfObjects_keypath_of(type, ...)\
    (keypathof_impl(@unionOfObjects., , type, __VA_ARGS__))
#define distinctUnionOfObjects_keypath_of(type, ...)\
    (keypathof_impl(@distinctUnionOfObjects., , type, __VA_ARGS__))
#define unionOfArrays_keypath_of(type, ...)\
    (keypathof_impl(@unionOfArrays., , type, __VA_ARGS__))
#define distinctUnionOfArrays_keypath_of(type, ...)\
    (keypathof_impl(@distinctUnionOfArrays., , type, __VA_ARGS__))
#define distinctUnionOfSets_keypath_of(type, ...)\
    (keypathof_impl(@distinctUnionOfSets., , type, __VA_ARGS__))
#define weak_keypath_count_of(...)\
    (weak_keypathof_impl(, .@count, __VA_ARGS__))
#define avg_weak_keypath_of(...)\
    (weak_keypathof_impl(@avg., , __VA_ARGS__))
#define max_weak_keypath_of(...)\
    (weak_keypathof_impl(@max., , __VA_ARGS__))
#define min_weak_keypath_of(...)\
    (weak_keypathof_impl(@min., , __VA_ARGS__))
#define sum_weak_keypath_of(...)\
    (weak_keypathof_impl(@sum., , __VA_ARGS__))
#define unionOfObjects_weak_keypath_of(...)\
    (weak_keypathof_impl(@unionOfObjects., , __VA_ARGS__))
#define distinctUnionOfObjects_weak_keypath_of(...)\
    (weak_keypathof_impl(@distinctUnionOfObjects., , __VA_ARGS__))
#define unionOfArrays_weak_keypath_of(...)\
    (weak_keypathof_impl(@unionOfArrays., , __VA_ARGS__))
#define distinctUnionOfArrays_weak_keypath_of(...)\
    (weak_keypathof_impl(@distinctUnionOfArrays., , __VA_ARGS__))
#define distinctUnionOfSets_weak_keypath_of(...)\
    (weak_keypathof_impl(@distinctUnionOfSets., , __VA_ARGS__))

Usage 1:

// @"param_"
id value = [self valueForKey:self_keyof(param_)];

// @"items_.firstObject"
id value = [self valueForKeyPath:self_keypathof(items_, firstObject)];

// @"selection.value"
id value = [self.arrayController_ valueForKeyPath:weak_keypathof(selection, value)];

// @"@sum.floatValue"
id sum = [self.items_ valueForKeyPath:sum_keypath_of(NSNumber*, floatValue)];

Usage 2:

@implementation SomeObject
+ (NSSet<NSString*>*)keyPathsForValuesAffectingDerivedValue
{
    // Can't use self_keypathof() in the class method ...*1
    return [NSSet setWithObjects:keypathof(SomeObject*, coeff_), keypathof(SomeObject*, items_, firstObject), nil];
}
@end

*1: To achieve to use self_keypathof in the class method, we can implement with below.

#define self_keyof(property)\
    (keyof(typeof([self self]), property))
#define self_keypathof(...)\
    (keypathof(typeof([self self]), __VA_ARGS__))

This implementation can be done with Xcode 15. However, it cause a compile error with Xcode 10.1 (the only prior version, I have). So, breaking the backward compatibility.

Edited 2024/01/03

Compile-time keypath safety macro for Objective-C

When using KVO or KVC in Objective-C, we encounter opportunities where we have to write a property name as a string, such as valueForKey:, valueForKeyPath:, willAccessValueForKey:, willChangeValueForKey:, etc.

Specifying a property name as a string means that if the property name is changed, the string part must also be explicitly changed at the same time. If the modification is forgotten, the compiler will not warn us and the code will crash at run-time.

Using NSStringFromSelector() and @selector() together as follows makes the compiler to be able to issue a warning. So, this is a famous solution.

Famous code:

// Use NSStringFromSelector() with @selector()
[self valueForKey:NSStringFromSelector(@selector(param_))];

While this technique is valid, it will cost some little overhead because NSStringFromSelector() operates at run-time. And also, it can't handle keypaths. So, it's time to think about techniques which enable compile-time warnings if possible without using NSStringFromSelector().

Are there any techniques to take advantage of the warning function in @selector(), and at the same time specify a property name as a string? See below codes.

How this works:

// Activate compile-time keypath check with @selector()
// Single level keypath
[self valueForKey:((void)@selector(param_), @"param_")];

// Multi level keypath
[self valueForKeyPath:((void)@selector(items_), (void)@selector(firstObject), @"items_.firstObject")];

In this example, the return values of @selector()s are cast to void and ignored, while the commas are used to join the syntaxes and express a property string. Also, since the result must represent a single string, the entire syntaxes are enclosed in ().

For multi level keypaths, it seems good to gather @selector()s in front and return a joined string at the end.

To achieve this usefully, we can take the way to implement codes as below by using the technique of pseudo-recursible macro.

One thing to consider, a problem is caused by the warning function in @selector(), we can't avoid the property name contamination because it is indistinguishable from a property name that validly exists in another class. However, this implementation is a realistic solution, I think.

Implementation:

// General use pseudo-recursible macro algorithm (maximum 16 level)
#define VA_ARGS_CONCAT_IMPL(x, y)\
    x##y
#define VA_ARGS_CONCAT(x, y)\
    VA_ARGS_CONCAT_IMPL(x, y)
#define VA_ARGS_COUNT_LOOKUP()\
    16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#define VA_ARGS_COUNT_INDEX(a1, a2, a3, a4, a5, a6, a7, a8,\
    a9, a10, a11, a12, a13, a14, a15, a16, n, ...)\
    n
#define VA_ARGS_COUNT_IMPL(...)\
    VA_ARGS_COUNT_INDEX(__VA_ARGS__)
#define VA_ARGS_COUNT(...)\
    VA_ARGS_COUNT_IMPL(__VA_ARGS__, VA_ARGS_COUNT_LOOKUP())
#define VA_ARGS_FOR_1(f, g, a)\
    f(a)
#define VA_ARGS_FOR_2(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_1(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_3(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_2(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_4(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_3(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_5(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_4(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_6(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_5(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_7(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_6(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_8(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_7(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_9(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_8(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_10(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_9(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_11(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_10(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_12(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_11(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_13(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_12(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_14(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_13(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_15(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_14(f, g, __VA_ARGS__))
#define VA_ARGS_FOR_16(f, g, a, ...)\
    g(f(a), VA_ARGS_FOR_15(f, g, __VA_ARGS__))
#define VA_ARGS_FOR(...)\
    VA_ARGS_CONCAT(VA_ARGS_FOR_, VA_ARGS_COUNT(__VA_ARGS__))

// Utility macros
#define VA_ARGS_GLUE_NONE(x, y)\
    x y
#define VA_ARGS_GLUE_SEMICOLON(x, y)\
    x; y
#define VA_ARGS_GLUE_COMMA(x, y)\
    x, y
#define VA_ARGS_GLUE_STRING_DOT(x, y)\
    x @"." y

// KeyPath safety macro algorithm (Core)
#define keyof_impl_selector(param)\
    (void)@selector(param)
#define keyof_impl_string(param)\
    @#param
#define keyof(param)\
    (keyof_impl_selector(param), keyof_impl_string(param))
#define keypathof_impl(prefix, suffix, ...)\
    VA_ARGS_FOR(__VA_ARGS__)(keyof_impl_selector, VA_ARGS_GLUE_COMMA, __VA_ARGS__),\
    prefix VA_ARGS_FOR(__VA_ARGS__)(keyof_impl_string, VA_ARGS_GLUE_STRING_DOT, __VA_ARGS__) suffix
#define keypathof(...)\
    (keypathof_impl(, , __VA_ARGS__))

// KeyPath safety macro algorithm (Extended)
#define keypath_count_of(...)\
    (keypathof_impl(, @".@count", __VA_ARGS__))
#define avg_keypath_of(...)\
    (keypathof_impl(@"@avg.", , __VA_ARGS__))
#define max_keypath_of(...)\
    (keypathof_impl(@"@max.", , __VA_ARGS__))
#define min_keypath_of(...)\
    (keypathof_impl(@"@min.", , __VA_ARGS__))
#define sum_keypath_of(...)\
    (keypathof_impl(@"@sum.", , __VA_ARGS__))
#define distinctUnionOfObjects_keypath_of(...)\
    (keypathof_impl(@"@distinctUnionOfObjects.", , __VA_ARGS__))
#define unionOfObjects_keypath_of(...)\
    (keypathof_impl(@"@unionOfObjects.", , __VA_ARGS__))
#define distinctUnionOfArrays_keypath_of(...)\
    (keypathof_impl(@"@distinctUnionOfArrays.", , __VA_ARGS__))
#define unionOfArrays_keypath_of(...)\
    (keypathof_impl(@"@unionOfArrays.", , __VA_ARGS__))
#define distinctUnionOfSets_keypath_of(...)\
    (keypathof_impl(@"@distinctUnionOfSets.", , __VA_ARGS__))

Usage 1:

// @"param_"
id value = [self valueForKey:keyof(param_)];

// @"items_.firstObject"
id value = [self valueForKeyPath:keypathof(items_, firstObject)];

// @"@sum.self"
id sum = [self.items_ valueForKeyPath:sum_keypath_of(self)];

Usage 2:

+ (NSSet<NSString*>*)keyPathsForValuesAffectingDerivedValue
{
    return [NSSet setWithObjects:keypathof(coeff_), keypathof(items_, firstObject), nil];
}

Usage 3:

static void* s_context = &s_context;

- (instancetype)init
{
    if ((self = [super init]) != nil) {
        [self addObserver:self forKeyPath:keypathof(value_) options:0 context:s_context];
        [self addObserver:self forKeyPath:keypath_count_of(items_) options:0 context:s_context];
    }
    return self;
}

- (void)dealloc
{
    [self removeObserver:self forKeyPath:keypath_count_of(items_) context:s_context];
    [self removeObserver:self forKeyPath:keypathof(value_) context:s_context];
    return;
}

- (void)observeValueForKeyPath:(nullable NSString*)keyPath
                      ofObject:(nullable id)object
                        change:(nullable NSDictionary<NSKeyValueChangeKey, id>*)change
                       context:(nullable void*)context
{
    if (context == s_context) {
        if (object == self) {
            if ([keyPath isEqualToString:keypathof(value_)]) {
                // do something...
            }
            else if ([keyPath isEqualToString:keypath_count_of(items_)]) {
                // do something...
            }
        }
    }
    else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
    return;
}

Limitation:

// NG
id count = [self.items_ valueForKey:keyof(@count)];
// OK
id count = [self.items_ valueForKeyPath:keypath_count_of(self)];

// NG
id avg = [self valueForKeyPath:keypathof(items_, @avg, self)];
// OK
id avg = [self.items_ valueForKeyPath:avg_keypath_of(self)];
// OK (alternative, more complex, run-time)
id avg = [self valueForKeyPath:[NSString stringWithFormat:@"%@.@avg.%@", keyof(items_), keyof(self)]];

Edited 2023/05/16

To use keyPathsForValuesAffecting<key> with NSObjectController family

When describing the relationship between dependent properties using KVO, it is well known that the compatibility between a class which inherits NSObjectController, and keyPathsForValuesAffectingValueForKey: or keyPathsForValuesAffecting<key> is worse.

Specifically, changes in selectedObjects can't be handled automatically, such as when implementing derived properties that depends on the value of selectedObjects of NSArrayController.

We can implement the goal by explicitly observing property changing with addObserver and removeObserver, but it is unsatisfactory.

Why do only the properties of a class which inherits NSObjectController not work properly? This seems to be due to the specification that NSKeyValueObservance class used internally by NSObjectController always handles the context value as nil.

Example that don't work:

@property (strong) NSArrayController* ac_;
@property (strong, readonly) NSString* secondValue;

- (nullable NSString*)secondValue
{
    NSString* result = nil;

    if (self.ac_.selectedObjects.count >= 2) {
        result = self.ac_.selectedObjects[1];
    }
    return result;
}

+ (NSSet<NSString*>*)keyPathsForValuesAffectingSecondValue
{
    return [NSSet setWithObjects:@"ac_.selectedObjects", nil];
}

Therefore, to fix this problem, make a subclass inherited from NSObjectController and override addObserver and removeObserver like below.

Its point is when the observer is a kind of NSKeyValueObservance class, the context value is always overridden as nil.

Modified ArrayController:

@interface ModArrayController : NSArrayController
@end

@implementation ModArrayController
- (void)addObserver:(NSObject*)observer
         forKeyPath:(NSString*)keyPath
            options:(NSKeyValueObservingOptions)options
            context:(void*)context
{
    [super addObserver:observer
            forKeyPath:keyPath
               options:options
               context:([observer isKindOfClass:NSClassFromString(@"NSKeyValueObservance")]) ? (nil) : (context)];
    return;
}

- (void)removeObserver:(NSObject*)observer
            forKeyPath:(NSString*)keyPath
               context:(void*)context
{
    [super removeObserver:observer
               forKeyPath:keyPath
                  context:([observer isKindOfClass:NSClassFromString(@"NSKeyValueObservance")]) ? (nil) : (context)];
    return;
}

- (void)removeObserver:(NSObject*)observer
            forKeyPath:(NSString*)keyPath
{
    NSMutableDictionary* dictionary;
    
    dictionary = NSThread.currentThread.threadDictionary;
    if ([observer isKindOfClass:NSClassFromString(@"NSKeyValueObservance")] &&
            dictionary[@"NSKeyValueObservanceLockKey"] == nil) {
        dictionary[@"NSKeyValueObservanceLockKey"] = @YES;
        [super removeObserver:observer forKeyPath:keyPath context:nil];
        [dictionary removeObjectForKey:@"NSKeyValueObservanceLockKey"];
    }
    else {
        [super removeObserver:observer forKeyPath:keyPath];
    }
    return;
}
@end

Use above modified class instead of NSArrayController. Just this solves the compatibility problem with keyPathsForValuesAffectingValueForKey: and keyPathsForValuesAffecting<key>.

This technique can be applied not only to NSArrayController, but also to any class which inherits NSObjectController.

Edited 2023/05/16

Recoverable DC Circuit Breaker

+1.8V ~ 28.0V で使用可能な直流電源用の過電流遮断器。

電源と負荷のあいだに挿入することにより、10mΩ の電流検出抵抗を介して過電流を検出し、出力 MOSFET を遮断する。トランジスタで組まれたラッチ回路により遮断状態は保持され、リセットスイッチを押すか、電源をオフにすることにより導通が復活する。

VR1 の半固定抵抗により制限電流値を最大 2.4A の範囲で設定できるが、基板の寸法を小さく設計したため 2A 程度が実用範囲である。電流検出部は、D1, D2 による簡易的な安定化電源により駆動されるため、2.4V 以下の入力時には、入力電圧の影響を受け制限電流値が低下する。

乾電池などを利用してモーターを駆動する回路での過電流制限や、ブレッドボード上での不注意による部品破損などを防止する目的で設計した。また、電流検出部を除き、ラッチ回路部分をすべて汎用的なディスクリート部品で設計した。

手ハンダで両面実装を楽しめるちょうど良いサイズである。

Schematic

Wiring

Gerber - top

Gerber - bottom

PCB - top x6

PCB - bottom x6

Index Value Model Mfr.Manufacturer @
R1 47K ohm (Thick Film Chip, 0805) (KOA) 1
R2 3.3K ohm (Thick Film Chip, 0805) (KOA) 1
R3 10K ohm (Thick Film Chip, 0805) (KOA) 1
R4 1K ohm (Thick Film Chip, 0805) (KOA) 1
R5, R7, R8, R9, R10, R14, R15 56K ohm (Thick Film Chip, 0805) (KOA) 7
R6, R11, R13, R16, R17 20K ohm (Thick Film Chip, 0805) (KOA) 5
R12 100K ohm (Thick Film Chip, 0805) (KOA) 1
R18 10m ohm PMR18EZPFU10L0 ROHM 1
VR1 10K ohm ST-4ETB103 Nidec Copal 1
C1, C2 10uF GRM21BR6YA106KE43 Murata 2
C3, C6 0.1uF CGA4J2X7R1H104K125AA TDK 2
C4 0.33uF CGA4J2X7R1H334K125AA TDK 1
C5, C7 0.47uF CGA4J3X7R1H474K125AB TDK 2
D1, D9 2mA S-202T SEMITEC 2
D2 2.4V UDZVTE-172.4B ROHM 1
D3, D4, D5, D6, D7, D8 RB501VM-40TE-17 ROHM 6
D10 Red GM4ZR83232AE SHARP 1
D11 18V UFZVTE-1718B ROHM 1
Q1, Q2, Q6, Q7, Q8 2SC2712-GR TOSHIBA 5
Q3, Q4, Q5, Q9 2SA1162-GR TOSHIBA 4
Q10 TPCA8128,LQ(M TOSHIBA 1
U1 INA190A2QDCKRQ1 Texas Instruments 1
U2 NJU7109F JRC 1
SW1 THAU13-AB-R Zhejiang Jianfu 1
(JP1), (JP2) OPT
CCXMonitor + CCXLive

Communication Cube X のための openFrameworks アプリケーション。CCXMonitor は、各個体のテレメトリデータを監視する。CCXLive は、各個体のキー押下状態を監視し、擬似キー押下信号を送信することができる。