Loading presentation...

Present Remotely

Send the link below via email or IM

Copy

Present to your audience

Start remote presentation

  • Invited audience members will follow you as you navigate and present
  • People invited to a presentation do not need a Prezi account
  • This link expires 10 minutes after you close the presentation
  • A maximum of 30 users can follow your presentation
  • Learn more about this feature in our knowledge base article

Do you really want to delete this prezi?

Neither you, nor the coeditors you shared it with will be able to recover it again.

DeleteCancel

Portable Code - the trials of porting Total War from Windows (v1.2)

No description
by

Tom Miles

on 21 May 2015

Comments (0)

Please log in to add your comment.

Report abuse

Transcript of Portable Code - the trials of porting Total War from Windows (v1.2)

CA::String(const char*);
CA::UniString(const UniChar*);
enum.cpp:1:6: error: ISO C++ forbids forward references to 'enum' types
enum Blib;
^
Portable Code - the trials of porting Total War from Windows to OS X
A Tale of Two (five actually) compilers
XCode 4.1
“The good thing about standards is that there are so many to choose from.”
Guy Davidson & Tom Miles
friend MY_CLASS should be friend class MY_CLASS

guy@creative-assembly.com
tom@creative-assembly.com
@Hatcat01
@teknogrebo
#include <> vs #include “” and the one true slash
class Blib
{
friend Blob;
private:
int data;
};

class Blob
{
public:
void copy(const Blib& blib) { data = blib.data; }
private:
int data;
};

int main(int argv, char** argc)
{
Blib a;
Blob b;

b.copy(a);
}



Clang
friends.cpp:3:10: error: C++ requires a type specifier for all declarations
friend Blob;
~~~~~~ ^
friends.cpp:3:10: error: friends can only be classes or functions
friends.cpp:11:45: error: 'data' is a private member of 'Blib'
void copy(const Blib& blib) { data = blib.data; }
^
friends.cpp:5:6: note: declared private here
int data;
^
3 errors generated.

std::sort requires const parameters
#include <vector>
#include <algorithm>

struct Blib
{
int data;
};

struct BlibLess
{
bool operator () (Blib& lhs, Blib& rhs) { return lhs.data < rhs.data; }
};

int main(int argv, char** argc)
{
std::vector<Blib> blibVec;
std::sort(blibVec.begin(), blibVec.end(), BlibLess());
}
Clang
In file included from op.cpp:2:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/algorithm:62:
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_algo.h:2263:11:
error: no matching function for call to object of type 'BlibLess'
while (__comp(*__first, __pivot))
^~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_algo.h:2296:19: note: in instantiation of function template specialization 'std::__unguarded_partition<__gnu_cxx::__normal_iterator<Blib *, std::vector<Blib, std::allocator<Blib> > >, Blib, BlibLess>' requested here
return std::__unguarded_partition(__first + 1, __last, *__first, __comp);
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_algo.h:2337:11: note: in instantiation of function template specialization 'std::__unguarded_partition_pivot<__gnu_cxx::__normal_iterator<Blib *, std::vector<Blib, std::allocator<Blib> > >, BlibLess>' requested here
std::__unguarded_partition_pivot(__first, __last, __comp);
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_algo.h:5489:9: note: in instantiation of function template specialization 'std::__introsort_loop<__gnu_cxx::__normal_iterator<Blib *, std::vector<Blib, std::allocator<Blib> > >, long, BlibLess>' requested here
std::__introsort_loop(__first, __last,
^
op.cpp:17:7: note: in instantiation of function template specialization 'std::sort<__gnu_cxx::__normal_iterator<Blib *, std::vector<Blib, std::allocator<Blib> > >, BlibLess>' requested here
std::sort(blibs.begin(), blibs.end(), BlibLess());
^
op.cpp:11:7: note: candidate function not viable: 2nd argument ('const Blib') would lose const qualifier
bool operator() (Blib& lhs, Blib& rhs) { return lhs.data < rhs.data; }
^
In file included from op.cpp:2:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/algorithm:62:
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_algo.h:2266:11: error: no matching function for call to object of type 'BlibLess'
while (__comp(__pivot, *__last))
^~~~~~
op.cpp:11:7: note: candidate function not viable: 1st argument ('const Blib') would lose const qualifier
bool operator() (Blib& lhs, Blib& rhs) { return lhs.data < rhs.data; }
^
2 errors generated.

GCC4.2
LLVM GCC4.2
Apple LLVM/Clang 2.1 (3.0 svn)
Intel
25.1.8 "The function object predicate shall not apply any non-constant function through the dereferenced iterator"
Use the macros to forward declare enums
enum Blib;

int main(int argv, char** argc)
{
Blib blob;
return 0;
}
enum.cpp:3:6: error: enumeration previously declared with fixed underlying type
enum Blib
^
enum.cpp:1:6: note: previous declaration is here
enum Blib : uint8_t;
^
};
calibs/redist/win/CAPlatform.h
#define DECLARED_ENUM(x, t) enum x

calibs/redist/OSX/CAPlatform.h
#define DECLARED_ENUM(x,t) enum x : t

#include "myfile.h"
#include <myfile.h>
"Causes the replacement of that directive by the entire contents of the source file identified by the specified sequence between the " delimiters. The named source file is searched for in an implementation-defined manner."
"Searches a sequence of implementation-defined places for a header identified uniquely by the specified sequence between the < and > delimiters"
WHO CARES!?!
#include "mypath\myfile.h"
#include "mypath/myfile.h"
If any constructor is explicitly declared, then no default constructor is automatically generated.
If a virtual destructor is explicitly declared, then no default destructor is automatically generated.
If a move constructor or move-assignment operator is explicitly declared, then:
No copy constructor is automatically generated.
No copy-assignment operator is automatically generated.
If a copy constructor, copy-assignment operator, move constructor, move-assignment operator, or destructor is explicitly declared, then:
No move constructor is automatically generated.
No move-assignment operator is automatically generated.
If a copy constructor or destructor is explicitly declared, then automatic generation of the copy-assignment operator is deprecated.
If a copy-assignment operator or destructor is explicitly declared, then automatic generation of the copy constructor is deprecated.
Beware of implicitly deleted and defaulted functions
class Blib
{
public:
Blib(int d) : data(d) {}
Blib(Blib&& rhs) : data(std::move(rhs.data)) {}
private:
int data;
};

int main(int argv, char** argc)
{
Blib a(42);
Blib b(a);
return 0;
}

rules.cpp:17:8: error: call to implicitly-deleted copy constructor of 'Blib'
Blib b(a);
^ ~
rules.cpp:7:3: note: copy constructor is implicitly deleted because 'Blib' has a
user-declared move constructor
Blib(Blib&& rhs) : data(std::move(rhs.data)) {}
^

Platform Abstractions
Emulation
Registry
C/C++ Runtime
Posix
Objective-C
Time and Date
Timers
File IO
Sockets
Windows
Cursors
Keyboard input
Mouse input
Harder Stuff
Concurrency
Maths and floating point
64 bit
Concurrency
Mutexes
Semaphores
Events
Critical Sections
Thread local storage
Atomics
Posix
Intrinsics/Carbon
}
C++11
Maths, Floating Points and Assembly
(oh my)
Don't pass non-aggregate types through varargs
A little history...
Where are they now...
Unity Files
Exclude all cpp files from build
Include all UnityXX.cpp files
Finally From Me...
Don’t rely on the implicit conversion operator for const safe pointer

template <typename TYPE> class CONST_SAFE_PTR
{
public:
CONST_SAFE_PTR(TYPE *rhs) : m_object(rhs) {}

const TYPE * operator () (void) const { return m_object; }
TYPE * operator () (void) { return m_object; }

operator const TYPE*() const { return m_object; }
operator TYPE*() { return m_object; }
protected:
TYPE * m_object;
};

int main(int argc, const char * argv[])
{
CONST_SAFE_PTR<int> p(new int);
delete p;
return 0;
}
main.cpp:30:5: error: ambiguous conversion of delete expression of type 'CONST_SAFE_PTR<int>' to a pointer
delete p;
^ ~
main.cpp:19:21: note: conversion to pointer type 'const int *'
operator const TYPE*() const { return m_object; }
^
main.cpp:20:21: note: conversion to pointer type 'int *'
operator TYPE*() { return m_object; }

printf(...)
safe format
unsafe format
Finding a Toolset
Caveats (and excuses)
GCC Compliance

MSVC Compliance
XCode 5.1
Workspace/Solution Setup
Visual Studio Workspace - 143 Projects
28 projects for main game
4 configurations per project
EXCLUDED_SOURCE_FILES & INCLUDED_SOURCE_FILES
XCode User Settings:
17 Main Projects
11 "Other" Libraries
Campaign
Battle
Multiplayer
Graphics
Audio
CALibs
Empire
TWLaunch
Windows include paths:
calibs/redist
calibs/redist/win

OSX include paths:
calibs/redist
calibs/redist/osx

June 13 2000
August 20 2002
September 22 2004
November 11 2006
March 3 2009
February 23 2010
March 15 2011
March 23 2012
September 3 2013
February 17 2015
TW3
Modular, Windows, mainly first party
0xC0000017,
out of quota
Running out of memory!?
Three products, new platforms...?
Steam/Linux
OS X
Rather busy, please help me Tom!
A Multilingual Codebase
Objective-C
(Cocoa)

C++
Templates
Breadth-first vs Depth-first
template <typename T>
void UNIT::notify_event(const T& p1)
{
m_faction.notify(p1)
m_army.notify(p1)
}

Do not reuse parameter IDs
template<typename FOO, typename BAR>
class PROJECTILE
{
public:
typedef std::vector<FOO> FOO;
};
template<typename
FOO_IN
, typename BAR>
class PROJECTILE
{
public:
typedef std::vector<
FOO_IN
> FOO;
};
Do not reuse parameter IDs
template<typename TASK, typename INFO>
struct TASK_SPECIFICATION
{
public:
typedef INFO INFO;
};
template<typename TASK, typename
INFO_T
>
struct TASK_SPECIFICATION
{
public:
typedef
INFO_T
INFO;
};
Qualify parent members
template<uint32_t SIZE>
class ANIM_ALLOCATOR : public E_STACK<SIZE>
{
uint32_t anim;
bool valid() { return frames.empty() && anim > 0; }
};
template<uint32_t SIZE>
class ANIM_ALLOCATOR : public E_STACK<SIZE>
{
uint32_t anim;
bool valid() { return
this->
frames.empty() && anim > 0; }
};
Reintroduce parent typedefs
template<uint32_t SIZE>
class ANIM_ALLOCATOR : public E_STACK<SIZE>
{
BUCKET* get_bucket(uint32_t idx) { ... }
};
template<uint32_t SIZE>
class ANIM_ALLOCATOR : public E_STACK<SIZE>
{

typedef typename E_STACK<SIZE>::BUCKET BUCKET;
BUCKET* get_bucket(uint32_t idx) { ... }
};
asm > c (for convenience)
Different versions of OSX produce different trigonometry results
Specialise outside class scope
struct MAP
{
template<typename T> void clear_assets(T& node_type);
template <> void clear_assets<uint32_t>(uint32_t& node_type)
{ ... }
};

struct MAP
{
template<typename T> void clear_assets(T& node_type);
};
template <>
void MAP::clear_assets<uint32_t>(uint32_t& node_type)
{ ... }
Dependent names
"This section is incomplete
Reason: reword to maybe make it more clear (or at least less intimidating), and while at it, apply CWG issue 591"
Disambiguate dependent names
template<typename T>
uint32_t UNIT::respond(const T& event)
{
vector<T>::iterator res = responses.begin();
};
template<typename T>
uint32_t UNIT::respond(const T& event)
{

typename
vector<T>::iterator res = responses.begin();
};
Disambiguate dependent names
template<typename M, typename B>
uint32_t make_link(M& model_object)
{
B& link = model_object.join<B>();
return link.idx();
}
template<typename M, typename B>
uint32_t make_link(M& model_object)
{
B& link = model_object.
template
join<B>();
return link.idx();
}
Eliminate circular references
template<typename FOO>
void fix_up(FOO& f)
{
AMMUNITION* am = f.ammo();
am->fixup(f);
}
Explicitly instantiate function templates
template <typename T>
float my_func(const T& t) { t.to_float(); }

float (*func)(const MY_CLASS&) = &my_func<MY_CLASS>;

template <>
float my_func<MY_CLASS>(const MY_CLASS&);
MSVC/Intel Notation
GCC/AT&T Notation
File IO
64 bit
20 GB of data, 150000 files
Create/write permissions
VFS + OSFS
File mapping
fopen/fclose takes time
Munge
Map
CA::UniChar = wchar_t
OpenGL consumes process space
High penetration of 64 bit OS X
ILP32, LLP64, LP64
Pointers don't hash to uint32_t
"using namespace"?
Not in my codebase!
std::distance, std::advance
size_t - unsigned int or unsigned long?
/tw/branches/attila/celts/common/CALibs/src/OSX/CACursor.cpp:71:28: error: expected ';' after expression
m_faction.notify(p1)
^
;
/tw/branches/attila/celts/common/CALibs/src/OSX/CACursor.cpp:72:37: error: expected ';' after expression
m_army.notify(p1)
^
;
2 errors generated.

/tw/branches/projectile.cpp:73:32: error: declaration of 'FOO' shadows template parameter
typedef std::vector<FOO> FOO;
^
/tw/branches/projectile.cpp:70:24: note: template parameter is declared here
template <typename FOO>
^

/tw/branches/attila/task.cpp:73:19: error: declaration of 'T' shadows template parameter
typedef INFO INFO;
^
/tw/branches/attila/task.cpp:70:24: note: template parameter is declared here
template <typename TASK, typename INFO>
^

/tw/branches/attila/animation.cpp:80:31: error: use of undeclared identifier 'frames'
bool valid() { return frames.empty() && anim > 0; }
/tw/branches/attila/animations.cpp:81:9: error: unknown type name 'BUCKET'; did you mean 'SOCKET'?
BUCKET* get_bucket(uint32_t idx) {}
^~~~~~
SOCKET
15 months later...
Project Settings
Add file to source control
Added to unity file
Exclude file from being compiled
Visual Studio
XCode
- Property sheets
- Configuration Settings file
Project Autogeneration
Premake
+ Small footprint
+ Easy to understand documentation
+ Lua configuration scripts
+ Written in Lua
+ Easy to modify core engine
- Minimal support for XCode 4
- No Support for VS2010
CMake
- Couldn't understand it
64 bit optimisations
8TB of user address space
No need to unmap files
No allocation fragmentation worries
5 bits of indexing
Watch out for working set size
Process and delivery
The Other platform
BOB & CI
Build on One Button
From source, art and text...
...to uploads and DVD ISOs
Continuous Integration
Perforce trigger
Email notification
Many submissions
Four configurations
Reduce link time
Exploit LTO
100 submissions a day
Many submissions
Not limited to OS X
Preprocessor problems
Eager check-in
Lazy check-in
Fix, apologise, doughnuts
About 1% submission failure
Many submissions
Not many Macs in-house
Less familiarity
OS X build broken more often
What next?
Increase compat testing, min spec
Get on to CI earlier
Keep reinforcing coding standards
Educate
Evangelise
OF COURSE WE'RE HIRING!!!
Great staff retention
Massage
Ice cream Wednesday
The fridge of health
Gym membership
Top-tier BUPA
Pension matching
25 days holiday
Christmas shut-down
Academic publication support
Great wrap parties
Licenced
Bink
Awesomium
Steam

3rd Party
zlib
lzma
lua
(Carbon)
using namespace CA;
#ifdef FOUR_BYTE_WCHAR
#define _U(x) ux
#else
#define _U(x) Lx
#endif
Coding Standards
Andrew S. Tanenbaum
const Blib& lhs, const Blib& rhs
template<typename T0, typename T1>
static String format(const String& formatting, T0 p0, T1 p1)
{
String output(64);
card16 pos = 0;
if( append_param(output, formatting, pos, p0) == false ) return output;
if( append_param(output, formatting, pos, p1) == false ) return output;
append_rest(output, formatting, pos);
return output;
}
#ifdef TARGETING_WIN

#else
// Paul is evil
// Apparently we can read each bit of data that BASIC_STRING has as a seperate va_arg and use it
// to construct a new string
size_t len = va_arg(args, size_t);
size_t capacity = va_arg(args, size_t);
char * buf = va_arg(args, char *);

const String arg(buf, len);
#endif
const String arg = va_arg(args, const String);
The words "check", "xor", "and", and "not" are macros in XCode
Little Gotchas
Don’t use non-standard extensions or constructs
Sleep
/tw/branches/attila/celts/common/CALibs/src/OSX/CACursor.cpp:73:9: error: missing 'typename' prior to dependent type name 'std::vector<T>::iterator'
std::vector<T>::iterator res = responses.begin();
^~~~~~~~~~~~~~~~~~~~~~~~
typename
/tw/branches/attila/link.cpp:73:32: error: use 'template' keyword to treat 'join' as a dependent template name
B& link = model_object.join<B>();
^
CALibs
Utility
U.I.
curl
openssl
json
Wwise
FaceFX
Implement a compatibility layer in OpenGL
Make the DirectX shaders work
Make it run at a decent framerate
Fight against driver bugs
class Mutex : public MutexPlatform
{
public:
void acquire();
};
/tw/calibs/redist/Mutex.h
/tw/calibs/source/win/Mutex.cpp





/tw/calibs/source/osx/Mutex.cpp
void Mutex::acquire()
{
EnterCriticalSection(&mMutex);
}
void Mutex::acquire()
{
pthread_mutex_lock(&mutex);
}
/tw/calibs/redist/win/MutexPlatform.h





/tw/calibs/redist/osx/MutexPlatform.h





class MutexPlatform
{
protected:
CRITICAL_SECTION mMutex;
};
class MutexPlatform
{
protected:
pthread_mutex_t mutex;
};
Strings
CA::UniChar > CA::UniChar16
16 bit
32 bit
= char16_t
Regex Captures:
Visual Studio: %1
XCode: \1
Team skills
enum Blib : uint8_t;
enum Blib
{
blibtastic = 0
};

int main(int argv, char** argc)
{
Blib blob;
return 0;
}
: uint8_t
vararg.cpp:16:24: error: cannot pass non-trivial object of type 'String' to
variadic function; expected type from format string was 'char *'
[-Wnon-pod-varargs]
sprintf(buffer, "%s", str);
~~ ^~~

class String
{
public:
String() : data(nullptr) {}
virtual ~String() {}
private:
char* data;
};

int main(int argv, char** argc)
{
String str;
char buffer[256];
sprintf(buffer, "%s", str);
return 0;
}

Windows: milliseconds
Posix: seconds
Use usleep()
5 Millions Lines of Code
/tw/common/CALibs/assets.cpp:86:25: error: explicit specialization of 'clear_assets' in class scope
template <> void clear_assets<uint32_t>(uint32_t& node_type) { ... }
/tw/common/CALibs/src/ammo.cpp:84:9: error: unknown type name 'AMMUNITION'
AMMUNITION* am = f.ammo();
June 1984 2011
12 months later...
More More More
More C++11
New file macro:
How much do they know?
40 different languages in use!
Standard comments header
Fresh codebase!
SOME reuse
const firewall
Exception safety costs
Thread safety costs
32 bit
const firewall
Multiplayer mode
5K units, 10Hz
Deterministic state
Fully initialise objects
Network changes model
Eager requesting
USE THE LIBS! (LUKE)
const firewall
FOO* foo();
const FOO* foo() const;
FOO(FOO&);
N4372
Full transcript