Posts Tagged ‘Programming’

Raw Key Code for Function Keys

I figure out key codes for function keys from F1 to F12 on Mac.

  • F1 : 122
  • F2 : 120
  • F3 : 99
  • F4: 118
  • F5: 96
  • F6: 97
  • F7: 98
  • F8: 100
  • F9: 101
  • F10: 109
  • F11: 103
  • F12: 111

They are not related to ASCII table at all.
Code marked as ‘solution’ or ‘answer’ here doesn’t care of them. They are all converted to a printable characters, ^P. So, a table created with the method in the StackOverflow can’t differentiate each of the function keys. So, if you want to differentiate them, customize value saved there using the table above.

It turned out that the database solution, 4D, also had such values for the function keys.
So, it says that the values above is not only specific to my keyboard, Logitech  K760.

(It’s surprising that 4D is still alive.)

NSOperation과 Key-value Observing, 그리고 performSelector:…

NSOperation등이 편한 건 안다.
하지만 그것을 thread/operation간의 동작을 위해서, KVO와 함께 쓰면 도무지 누가 caller인지 알기가 힘들다.
NSOperation은 쓰레드 프로그래밍을 쉽게 하기 위한 것 외에도 multicore에 task를 어떻게 한 단위로 분산 시킬 것인지에 대한 고민에서 나온게 더 기본적 이유 아니냐?
쓴다고 무작정 다 쓸게 아니라, 왜 만들었는가에 대한 이해를 하고 써야 한다.

또한 NSOperation을 쓸 때, dependency를 사용하지 않고 막 쓰면서, 단순히 타이밍상 어떤 부분의 코드가 operation으로 지정되는 것보다 먼저 혹은 늦게 수행 될 것이 거의 100%라고 생각해서 dependency 안쓰고 막 작성하는 코드.. 디버깅시에 문제를 일으킨다.

새로운 API 무조건 쓴다고 좋은게 아니다.
뭐든 이해하고 잘 쓰는게 좋은거지.

하긴 레주메엔 뭐 써 봤고, 인터뷰 시에 그거 써본 경험 이야기하면 “잘 아는 사람”으로 인지시킬 순 있겠지.

NSNotificationCenter with Blocks Considered Harmful

This is worth while to being shared!
http://sealedabstract.com/code/nsnotificationcenter-with-blocks-considered-harmful/

Xcode 5 creates properties ( member variables ) for @property like this

For people who doesn’t trust me.

This is original behavior of creating a member variable for a @property.

Why not to use ARC, from Pivotal labs

One of problems I felt with ARC was that… it actually makes thing more difficult.

Here is some good explanation.

So, it makes easy stuff easier, but difficult stuff more difficult.

Yes. That’s my gut feeling.

BTW, I don’t understand why people had difficulty in memory management. Probably new S/W engineers are spoiled already got used to easier languages which usually doesn’t require memory management?
At some companies, I noticed that they didn’t have good pattern in memory management. That is.. they allocated new memory space at any time and deallocated it at any time.

Rule of thumb is like this.
If an object is going to have a life span as long as its containing object or almost similar life span, try to allocate it when the containing object is created and release it when the containing object is to be released. In other words, allocate in a constructor and release it in destructor.

Second rule is that if it’s created in a function, try to release it in the function right after you finish using it.If a function is to return the newly allocated object, name the function to reflect that it creates a new object and will return the object.

Third rule is to make clear object ownership.

I have seen many functions and methods in which they create a new object but the method/function names don’t imply or state the fact. Then it becomes very hard to trace where one object is created and when it’s destructed. ( destroying is also same. )

If those simple rules are kept, memory management becomes very “manageable” and “easily understandable” when a new person to an existing project starts to work on it. Also it is very easy to debug and figure out from when to when they are alive.

A consistent pattern is very important to make piece of code manageable.

Difference between Objective-C compilation for Mac and iOS for 32 bit

For past several years, Apple people tried to make kernel for Mac and iOS identical.In Snow Leopard, they got rid of PowerPC code and started to introduce 64 bit kernel.

On Lion, as far as I know, kernel version of iOS caught up that of Mac OS X. So, after that it’s widely said that kernels for iOS and Mac are the same.
However, Apple recently introduced 64bit iOS for iOS 7. So, although the kernel for Mac was built as 64bit, iOS was built 32bit so far.
Anyway, the trend is that Cocoa becomes similar and Objective-C compiler becomes similar in feature list they support.

However, I noticed that 32bit compilation for Mac is not up-to-date compared to that for iOS.

Here is the difference between the two.

32bit compilation for Mac OS X (on the left) and iOS (on the right)

32bit compilation for Mac OS X (on the left) and iOS (on the right)

Although the overall direction is to make them identical on Mac OS X and iOS identical, it turns out that Apple didn’t update 32bit compilation for Mac to match that for iOS. The 64 bit runtime on Mac OS X is so called ‘modern run-time’. But should the run-time different for Mac and iOS?
I’m not sure because I don’t know the detailed story about what happened at Apple.

Because I have worked for iOS recently, i thought that 32 bit build environment for OS X would be the same to 32 bit environment for iOS. But it was not.
Apple’s official feature matrix can be found here.
What doesn’t catch is that modern runtime is only for 64 bit on Mac.

 

Unexpected behavior of Win32 when checking serial ports.

I recently noticed very interesting behavior in ClearCommError() function of Win32.
This code returns how many bytes there are in incoming buffer for a serial port.

DWORD CSerialPort::BytesWaiting()
{
  //Validate our parameters
  ASSERT(IsOpen());

  //Check to see how many characters are unread
  COMSTAT stat;
  GetStatus(stat);
  return stat.cbInQue;
}

void CSerialPort::GetStatus(COMSTAT& stat)
{
  //Validate our parameters
  ASSERT(IsOpen());

  DWORD dwErrors;
  if (!ClearCommError(m_hComm, &dwErrors, &stat))
  {
    DWORD dwLastError = GetLastError();
    TRACE(_T("CSerialPort::GetStatus, Failed in call to ClearCommError, Error:%d\n"), dwLastError);
    ThrowSerialException(dwLastError);
  }
}

However, after actually debugging it I found out that there are cases where

  • The number of bytes in its incoming buffer doesn’t increase while it is calling BytesWaiting() to see if it received enough bytes to read. Then, it should read its incoming buffer for the given COM port to have BytesWaiting() starts to return accumulated number of bytes after reading.
  • When BytesWaiting() returns 0, you still need to read the buffer not to fail in detecting any further data arrived from the other side of communication. reading 0 bytes in this case, will cause the BytesWaiting() keeps returning the number of bytes read after that point.

Normally, whether you read or not, callying BytesWaiting() correctly reports the number of bytes in an incoming buffer.
However, if any of those listed above happens, it stops reporting correct number of bytes.

So, this code fails.

int CLensControllerDlg::ReadIncomingPacket(BYTE incomingBuffer[], int numOfBytesToRead, bool *pShouldGiveUp)
{
     DWORD bytesInReceptionQueue;
     int readNumOfBytes, extraReadNumOfBytes;

     readNumOfBytes = extraReadNumOfBytes = 0;

     // First read
     readNumOfBytes = m_pSerialPort->Read( incomingBuffer, numOfBytesToRead );
     int lengthToReadFurther;
     int secondTryReadLength = 0;

     static int trialCount = 0;

     // Check if it needs to read once more
     if( (incomingBuffer[ 0] == (BYTE) 0x50) &&
           (incomingBuffer[ 1] == (BYTE) 0xAF ))
     {
           lengthToReadFurther = static_cast<int >(incomingBuffer[4]) + 1; // payload length + check sum
            // Read length + 1 more ( 1 is for checksum )

            //!!!!! [1] !!!!!
            // There are cases where bytesInReceptionQueue doesn't have accumulated number of
            // bytes in a reception bufffer.
            while( (bytesInReceptionQueue = m_pSerialPort->BytesWaiting()) < lengthToReadFurther )
           {
                Sleep( 3 ); // 10 msec
                trialCount++;

                //!!!!! [2] !!!!!
                // So, if the bytesInReceptionQueue doesn't contain the accumulated number of bytes
                // through the iteration, try to read some.
                // Then it will start to work again.
                 if( trialCount > 10 )
                {
                      if( bytesInReceptionQueue > 0 )
                     {
                            // Give another chance
                           secondTryReadLength = m_pSerialPort->Read( &incomingBuffer[readNumOfBytes], bytesInReceptionQueue );
                           readNumOfBytes += secondTryReadLength;

                           lengthToReadFurther -= secondTryReadLength;

                           trialCount = 0;

                     }
                      else
                     {
                            // let's see what it happens if try reading for 0 bytes and read further
                           BYTE temp[ 16];
                            int tempReadLength;

                            // bytesInReceptionQueue == 0
                           tempReadLength = m_pSerialPort->Read( &incomingBuffer[readNumOfBytes], bytesInReceptionQueue );
                           readNumOfBytes += tempReadLength;
                           lengthToReadFurther -= tempReadLength;

                            //// Once read, somehow, bytesInReceptionQueue starts to be accumulated again.
                            //bytesInReceptionQueue = m_pSerialPort->BytesWaiting();

                            //tempReadLength = m_pSerialPort->Read( &incomingBuffer[readNumOfBytes], bytesInReceptionQueue );
                            //readNumOfBytes += tempReadLength;
                            //lengthToReadFurther -= tempReadLength;

                           CString msgString;
                           msgString.Format( _T( ">> Will give up this incoming data : %x %x %x %x %x\r\n"),
                                                  incomingBuffer[0], incomingBuffer[1 ],
                                                  incomingBuffer[2], incomingBuffer[3 ],
                                                  incomingBuffer[4] );

                           WriteLogMessage( msgString );
                           TRACE( msgString );

                            if( pShouldGiveUp != NULL )
                           {
                                *pShouldGiveUp = true;
                           }

                           trialCount = 0;
                            return 0;
                     }
                }
           }

            // In the queue, now there are lengthToReadFurther or more to read.
           extraReadNumOfBytes = m_pSerialPort->Read( &incomingBuffer[readNumOfBytes], lengthToReadFurther );
     }

     return (readNumOfBytes + extraReadNumOfBytes);
}

The code above was written while I was debugging. So, it’s not clean nor compact.
So, here is better version if you want to see.

int CLensControllerDlg::ReadIncomingPacket(BYTE incomingBuffer[], int numOfBytesToRead, bool *pShouldGiveUp)
{
     DWORD bytesInReceptionQueue;
     int readNumOfBytes, extraReadNumOfBytes;

     readNumOfBytes = extraReadNumOfBytes = 0;

     // First read
     readNumOfBytes = m_pSerialPort->Read( incomingBuffer, numOfBytesToRead );
     int lengthToReadFurther;
     int secondTryReadNumOfBytes = 0;

     // Check if it needs to read once more
     if( (incomingBuffer[0] == (BYTE)0x50) &&
          (incomingBuffer[1] == (BYTE)0xAF ))
     {
          lengthToReadFurther = static_cast<int>(incomingBuffer[4]) + 1; // payload length + check sum
          // Read length + 1 more ( 1 is for checksum )
          while( lengthToReadFurther > 0 )
          {
               Sleep( 1 ); // 10 msec

               bytesInReceptionQueue = m_pSerialPort->BytesWaiting();

               // If it's normal, just waiting until bytesInReceptionQueue >= lengthToReadFurther,
               // and read the whole "lengthToReadFurther" amount of data all at once.
               //
               // However, sometimes the inbound buffer should be read to have further data accumulated.
               // If not,
               secondTryReadNumOfBytes = m_pSerialPort->Read( &incomingBuffer[readNumOfBytes], lengthToReadFurther );
               readNumOfBytes += secondTryReadNumOfBytes;

               lengthToReadFurther -= secondTryReadNumOfBytes;
          }
     }

     return readNumOfBytes;
}
%d bloggers like this: