Friday, 22 July 2011

Xcode: Determining the current SDK version in a script

In an iOS XCode project, I needed a shell script target (aka "External Build Tool" target) that invokes other scripts which in turn build stuff using Xcode. Curiously recursive.

Now, I needed to pass that script the current iOS SDK version being used so it could arrange its own build malarkey, and get all its SDK ducks in a row.

There is no environment variable for that defined by Xcode. Bah. The closest you get is the current deployment target version, which appears in $IPHONEOS_DEPLOYMENT_TARGET.

That seems like rather a large omission.

You do get given $SDKROOT which is a file path to the current SDK. But that's not quite the same thing as a simple version number.

Never, fear, shell gibberish to the rescue. This is what I came up with:

SDK_VERSION=$(echo $SDKROOT | sed -e 's/.*iPhone\(OS\)*\(Simulator\)*\([0-9]*.[0-9]*\).sdk/\3/')

If you know a better way to do this, I'd love to know.

Tuesday, 19 July 2011

C++: Declaring a pointer to a template method

Busy writing some template gibberish, we needed to make a healthy trade in pointers to template-member-functions. Of template classes. (Where the template function parameters themselves were pointers to template methods on template classes, but let's not worry about that detail right now).

It took a little run-up to get the C++ syntax right, so I present it here for your viewing pleasure.

All code tested against g++ 4.2.1 only.

Case 1: Normal pointer to member

Let's just remind ourselves of the syntax for a simple pointer to (normal, non-template) member function:

Note: I've gratuitously changed the < < stream operators into "--" just to get the syntax through blogger's composer window. Sorry about that. I pray that no other C++ syntax was sacrificed in this publishing exercise
class Target1
{
public:
void Method(int a)
{
std::cout -- "Target1(" -- a -- ")\n";
}
};

void PointerToNormalMemberFunction()
{
Target1 target;
// This is how we construct a normal pointer to member function
void (Target1::*oneParam)(int) = &Target1::Method;
(target.*oneParam)(1);
}
Relatively simple.

Case 2: A pointer to template member function

Here's the first incursion of templates. If you're looking at a template method, this is how you'd declare your pointers to it:
class Target2
{
public:
template <typename T>
void Method(T a)
{
std::cout -- "Target2("--a--")\n";
}
};

void PointerToTemplateMemberFunction()
{
Target2 target;

// This is how we construct a pointer to a template member function
// See how the template type of the method is mentioned at the end of the method name.
void (Target2::*oneParamTemplateInt)(int) = &Target2::Method<int>;
void (Target2::*oneParamTemplateFloat)(float) = &Target2::Method<float>;
(target.*oneParamTemplateInt)(2);
(target.*oneParamTemplateFloat)(2.5);

// However, the compiler can deduce the template type of the method
void (Target2::*shorterInt)(int) = &Target2::Method;
void (Target2::*shorterFloat)(float) = &Target2::Method;
(target.*shorterInt)(3);
(target.*shorterFloat)(3.5);
}
Note that you can chose whether or not to specify the template types of the method when you assign it to your member-function pointer. The compiler can deduce these for you.

Case 3: Pointer to template methods with more than one template parameter

This is not significantly different from the above, we just extend the types in the pointer-to-member.

class Target3
{
public:
template <typenameT>
void Method(T1 a, T2 b)
{
std::cout -- "Target3("--a--","--b--")\n";
}
};

void PointerToTemplateMemberFunctionWithTwoParameters()
{
Target3 target;

// This is how we construct a pointer to a template member function
// with multiple template parameters. Just like above, really.
void (Target3::*oneParamTemplateIntFloat)(int,float) = &Target3::Method<int,float>;
void (Target3::*oneParamTemplateFloatInt)(float,int) = &Target3::Method<float,int>;
(target.*oneParamTemplateIntFloat)(4,4.5);
(target.*oneParamTemplateFloatInt)(4.5,4);

// Again, the compiler can deduce the type of the methods
void (Target3::*shorterIntFloat)(int,float) = &Target3::Method;
void (Target3::*shorterFloatInt)(float,int) = &Target3::Method;
(target.*shorterIntFloat)(5,5.5);
(target.*shorterFloatInt)(5.5,5);
}
Again, note, the compiler can generally deduce the correct template method without you having to specify the template parameter types.

Case 4: Pointer to template methods in a template class.

Now it's getting sillier - a pointer to a template method in a template class. the syntax does still make sense, it just depends how far down the rabbit hole you want to go.

template<typename TYPE>
class Target4
{
public:
Target4(const TYPE &value) : value(value) {}
TYPE value;

template <typename T>
void OneParam(T a)
{
std::cout -- "Target4::OneParam("--value--","--a--")\n";
}

template <typename T1, typename T2>
void TwoParam(T1 a, T2 b)
{
std::cout -- "Target4::TwoParam("--value--","--a--","--b--")\n";
}
};

void PointerToTemplateMemberInTemplateClass()
{
Target4<char> target('c');

void (Target4<char>::*oneParam)(float) = &Target4<char>::OneParam<float>;
(target.*oneParam)(6.5);

// Again, we can miss off the last template types
void (Target4<char>::*shorter)(float) = &Target4<char>::OneParam;
(target.*shorter)(6.75);

// Two parameters just extends the scheme
void (Target4<char>::*twoParam)(float,int) = &Target4<char>::TwoParam;
(target.*twoParam)(6.8,6);
}

Case 5: Using a pointer to a template method of a template class inside the template class itself

If you want to make use of a pointer to template method within a template class, you simply cannot specify the template method's parameter types. The compiler considers this a syntax error. So you have to rely on the compiler deducing the correct template method instantiation. (See edit below.)

In the case of this example, it copes fine. In more complex cases, it may hurt less if your call template method overloads different names.

template<typename TYPE>
class Target5
{
public:
Target5(const TYPE &value) : value(value) {}
TYPE value;

template <typename T>
void OneParam(T a)
{
std::cout -- "Target5::OneParam("--value--","--a--")\n";

typedef void (Target5<E>::*MethodTypeToCall)(T);
// Here, the compiler picks the right overload
MethodTypeToCall toCall = &Target5<E>::Private;
// In this case, the compiler does not let us write the following line (parse error):
//MethodTypeToCall toCall = &Target5<E>::Private<t;;
(this->*toCall)(a);
}

template <typename T1, typename T2>
void TwoParam(T1 a, T2 b)
{
std::cout -- "Target5::TwoParam("--value--","--a--","--b--")\n";

typedef void (Target5<E>::*MethodTypeToCall)(T1,T2);
MethodTypeToCall toCall = &Target5<E>::Private; // compiler picks the right overload
// you can't add the method's template parameters to the end of that line
(this->*toCall)(a,b);
}

private:

template <typename T>
void Private(T a)
{ std::cout -- "Target5::Private("--value--","--a--")\n"; }
template <typename T1, typename T2>
void Private(T1 a, T2 b)
{ std::cout -- "Target5::Private("--value--","--a--","--b--")\n"; }
};

void HoldingAPointerToTemplateMemberInTemplateClass()
{
Target5<r> target('c');

void (Target5<r>::*oneParam)(int) = &Target5<r>::OneParam;
(target.*oneParam)(7);
void (Target5<r>::*twoParam)(float,int) = &Target5<r>::TwoParam;
(target.*twoParam)(7.5,7);
}
Edit: it's been pointed to to me that you can name a specific overloaded template method using the following syntax. Add this to your pipe and smoke the whole template shenanigans:

MethodTypeToCall toCall2 = &Target5::template Private<T>;

This kind of template gibberish is why you know you love C++.

Simples, init?

The extra thing we added to all this syntactical joy was to have one of the template method's (template) parameter types itself a pointer to a template method on a template class.

It was at this point our brains dribbled out of our ears, and we had to retrace our template syntax steps back up this rabbit hole.

Monday, 18 July 2011

Writing: It's The Thought That Accounts

The July issue of ACCU's C Vu magazine is out now. It contains the latest instalment in my Becoming a Better Programmer column. This one's called It's The Thought That Accounts.

In it I describe how you can become a better programmer, and how you will be encouraged to write better code, through accountability. Far from being a dirty word, or some kind of bureaucratic nightmare, developer accountability can be fun, stimulating, enriching, and valuable.

You'll also find out about my wine and running predilections.

This is a great issue of C Vu, with some really interesting articles. If you're a developer who is not already an ACCU member, then I strongly urge you to join. It's super-cheap and really worthwhile!

I'll hold you accountable to that...