C/C++分類文章 顯示方式:簡文 | 列表

August 28,2009

會自殺的類別

坦白說,我從沒想過物件可以自殺這件事情。當我看到這樣寫的時候,實在是很令我驚訝...實驗的結果,還真的是可以。
#include <iostream>

class SuicideSample {
        public:
                SuicideSample() { std::cout << "ctor." << std::endl; }
                ~SuicideSample() { std::cout << "dtor." << std::endl; }
                void DoIt( void ) { 
                        delete this;
                }
};

int main( int argc, char* argv[] )
{
        SuicideSample* obj=new SuicideSample();
        obj->DoIt();
        // of course, object can kill itself, but if you try to kill it again
        // program will crash.
        //delete obj;
        return 0;
}

Posted by elleryq at 樂多Roodo!16:19回應(2)引用(0)
標籤:c++

June 29,2009

Skia and framebuffer

根據 Jserv 大的淺談 Google Skia 圖形處理引擎,得知 skia 只能畫在 Memory buffer 上,那麼,可以直接畫在 Framebuffer 上嗎??

Jserv 大文章裡的例子,SkBitmap 得先呼叫 allocPixels() 來配置所需要的 Memory buffer,根據 SkBitmap.h 裡的宣告,allocPixels 事實上是使用 allocator 來配置所需要的 Memory buffer,如果未指定,會以 stdalloc(HeapAllocator) 來進行配置。因此如果要直接使用 framebuffer,可以繼承 SkBitmap::Allocator 類別之後,改寫 allocPixelRef() 來達到目的。
大致的代碼就像這樣子:
#include "SkTypes.h"
#include "SkRefCnt.h"
#include "SkBitmap.h"
#include "SkDevice.h"
#include "SkPaint.h"
#include "SkRect.h"
#include "SkMallocPixelRef.h"

class FrameBufferAllocator: public SkBitmap::Allocator
{
    public:
	FrameBufferAllocator();
	virtual ~FrameBufferAllocator();
        virtual bool allocPixelRef(SkBitmap*, SkColorTable*);
    private:
        char* m_addr;
        int fd;
};

FrameBufferAllocator::FrameBufferAllocator()
{
}

FrameBufferAllocator::~FrameBufferAllocator()
{
  // munmap framebuffer pointer.
  // close the framebuffer device we opened
}

bool FrameBufferAllocator::allocPixelRef(SkBitmap* dst, SkColorTable* ctable)
{
    size_t size = dst->getSize();

    // open framebuffer
    fd = open( "/dev/fb0", O_RDWR );

    // setup framebuffer device via ioctl.

    // mmap framebuffer
    m_addr = (char *)mmap(0, screensize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

    // call original procedures in HeapAllocator::
    dst->setPixelRef(new SkMallocPixelRef(m_addr, size, ctable))->unref();
    dst->lockPixels();
    return true;
}
接著,Jserv 大的範例只要修改一行:
	bitmap.allocPixels( new FrameBufferAllocator, NULL );
就可以順利運作了。

p.s. 在 link libskia.a 時,遇到很鳥的狀況,skia 的 Makefile 在製作時,是直接以帶有路徑的 .o 去作,而我 toolchain 的 gcc 居然無法處理,必須要以沒有路徑的 .o 去重新製作,這樣才能 link 成功。

Posted by elleryq at 樂多Roodo!18:03回應(0)引用(0)
標籤:c++,embedded

June 26,2009

C 的 && 與 ||

前一陣子看Javascript 語言精髓與編程實踐這本書的時候,發現可以用 && 來達到 if ,用 || 達到 if not 的效果,所以下面是以 C/C++ 實驗的結果:
#include <stdio.h>

int say_hello( void )
{
    printf("Hello world!!\n");
    return 0;
}

int main( int argc, char* argv[] )
{
    int flag=0;

    printf("flag=0\n");
    flag && say_hello(); // say_hello() won't be invoked
    flag || say_hello(); // say_hello() will be invoked

    printf("flag=1\n");
    flag=1;
    flag && say_hello(); // say_hello() will be invoked
    flag || say_hello(); // say_hello() won't be invoked

    return 0;
}
不過坦白說,這樣寫的結果是導致可讀性變差,只有自己維護這份 code 時,那是可以用,很多人維護一份 code 時,最好還是避免,或者,加上註解比較好。 我想 c# / java 應該也可以這樣作。 p.s. 如果不是呼叫函數的話,記得要使用 ( ),例如:
i && (j=100);
這就等同於:
if(i) j=100;

Posted by elleryq at 樂多Roodo!22:35回應(3)引用(0)
標籤:c

May 8,2009

Linux I2C 與 AD71471

AD71471 的 Device address 是 0x58,Linux driver 在處理這個時,其實會自行左移一位,因此,在 I2C_SLAVE_FORCE 的 ioctl 裡,應該是要傳 0x2c。這邊因為我暈頭,把 0x58>>1 算成 0x4c,導致我搞了好一陣子,直到 M 同事指正以後,才弄對。

再來,AD71471 在做讀寫時,Register address 與 data 都是 2 bytes,而 i2ctools 裡,處理 Register address 都只傳 1 byte(I2C_SMBUS),因此不適用在 AD71471 上。我把 Linux kernel i2c-core.c 裡的 code 翻出來改寫,改用 I2C_RDWR 來處理:
bool Write( uint16_t address, uint16_t value ) {
	int res=0;
	struct i2c_rdwr_ioctl_data msg_rdwr;
	char msgbuf0[I2C_SMBUS_BLOCK_MAX+4];
	char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
	struct i2c_msg msg[1] = { { _address, 0, 4, msgbuf0 } };
	uint8_t* pAddr = (uint8_t*)&address;
	uint8_t* pValue = (uint8_t*)&value;

	msg_rdwr.msgs = &msg[0];
	msg_rdwr.nmsgs = 1; // write // read = 2

	// 因為 little endian,所以要作調整
	msgbuf0[0] = *(pAddr+1);
	msgbuf0[1] = *(pAddr+0);
	msgbuf0[2] = *(pValue+1); // (1)
	msgbuf0[3] = *(pValue+0);

	res = ioctl( _file, I2C_RDWR, &msg_rdwr );
	usleep(10000);

	return true;
}
bool Read( uint16_t address, uint16_t& data ) {
	int res=0;
	struct i2c_rdwr_ioctl_data msg_rdwr;
	char msgbuf0[I2C_SMBUS_BLOCK_MAX+2];
	char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]={0};
	struct i2c_msg msg[2] = { { _address, 0, 2, msgbuf0 },
	                          { _address, I2C_M_RD, 2, msgbuf1 }
	                        };
	uint8_t* pAddr = (uint8_t*)&address;

	msg_rdwr.msgs = &msg[0];
	msg_rdwr.nmsgs = 1; // read = 2
	// 因為 little endian,所以要作調整
	msgbuf0[0] = *(pAddr+1);
	msgbuf0[1] = *(pAddr+0);
	res = ioctl( _file, I2C_RDWR, &msg_rdwr );
	usleep(10000);

	msg_rdwr.msgs = &msg[1];
	msg_rdwr.nmsgs = 1; // read = 2
	res = ioctl( _file, I2C_RDWR, &msg_rdwr ); #ifdef DEBUG
	data = msgbuf1[0] | (msgbuf1[1] << 8);

	return true;
}
int main( int argc, char* argv[] )
{
	int data=0;

	Read( 0x17, data );
	printf("data=%d\n", data );

	// 這邊其實不好,實際上寫 0x0052會比較清楚,這邊必須寫 0x5200,因為我在 Write() 裡有作對調,參看(1)
	Write( 0x00, 0x5200 );  
}

Posted by elleryq at 樂多Roodo!11:57回應(0)引用(0)
標籤:linux,c++

May 7,2009

GtkDrawingArea 與 gtk_widget_set_events()

GtkDrawingArea 預設是不收 button_press_event 跟 key_press_event 的,所以要使用 gtk_widget_set_events() 告訴 GtkDrawingArea 要接收才行。

這個,我是去 Google Code Search 找來的,雖然 devhelp 可以查指令,但沒有範例,還是很難猜到怎麼用。
#include <gdk/gdkkeysyms.h> // 定義按鍵值的 header

static gboolean press_event( GtkWidget* widget, GdkEventButton* event, gpointer data )
{
	if( debug )
		g_print("press_event: x=%f y=%f button=%d\n", event->x, event->y, event->button );

	if( event->button==1 )  { // left
	}
}

static gboolean key_event( GtkWidget* widget, GdkEventKey* event )
{
	if( debug )
		g_print( "event->keyval=%d event->state=%d\n", event->keyval, event->state );
	switch (event->keyval) {
		// 省略...
	}
	return TRUE;
}

int main( int argc, char* argv[] )
{
// 省略一萬行...
	g_signal_connect( drawing_area, "button_press_event", G_CALLBACK( press_event ), NULL );
	g_signal_connect( drawing_area, "key_press_event", G_CALLBACK( key_event ), NULL );

	// 要接收 button_press_event 跟 key_press_event 喔~
	gtk_widget_set_events( drawing_area, gtk_widget_get_events(drawing_area) | GDK_BUTTON_PRESS_MASK | GDK_KEY_PRESS_MASK );
// 省略兩萬行...
}

Posted by elleryq at 樂多Roodo!20:13回應(0)引用(0)
標籤:linux,gtk,c

May 6,2009

gtk 載入並顯示圖片

實際上是用 GDK+GtkDrawingArea 來畫,所以在下面的程式片斷,你會看到我宣告了 GtkDrawingArea 並且實作了 GtkDrawingArea 的 expose 事件。 GDK 支援的圖片格式很多,常見的 jpg、png、bmp 都沒問題。
static gboolean expose_event( GtkWidget* widget, GdkEventExpose* event, gpointer data )
{
	GError* error=NULL;
	int width=widget->allocation.width, height=widget->allocation.height;

	GdkPixbuf* buf=gdk_pixbuf_new_from_file_at_scale( "your_photo.jpg", &error );
	if( buf==NULL )
		g_print("load fail.\n" );
	else
	{
		bufWidth = gdk_pixbuf_get_width( buf );
		bufHeight = gdk_pixbuf_get_height( buf );
		gdk_draw_pixbuf( widget->window, NULL, buf, 0, 0, 0, 0, 
				(width>bufWidth?bufWidth:width), (height>bufHeight?bufHeight:height), 
				GDK_RGB_DITHER_NORMAL, 0, 0 );
		g_object_unref( buf );
	}
}

int main( int argc, char* argv[])
{
	GtkWidget* drawing_area=NULL;

	// 省略一萬行
	g_signal_connect( G_OBJECT(drawing_area), "expose_event", G_CALLBACK( expose_event ), NULL );

	// 再省略兩萬行...	
}

Posted by elleryq at 樂多Roodo!19:14回應(0)引用(0)
標籤:linux,gtk,c

March 10,2009

placement new()

昨天有同事問到可不可以讓物件 new 在 share memory 裡面,我跟他說 c++ 的 new 可以像下面例子這樣用,但是他後來沒試。好吧,反正我以前也沒試過,就寫了個小程式試一下:
/**
 *       Filename:  test_new.cpp
 *    Description:  Test new(storage) Person();
 */
#include <cstdlib>
#include <string>
#include <iostream>

class Person {
	public:
		Person() {}
		~Person() {}

		std::string getName() { return _name; };
		void setName( std::string name ) { _name=name; }

		std::string toString() { return _name; }
	private:
		std::string _name;
};

int main( int argc, char* argv[] ) {
	char* storage = (char*)malloc( 1024 ); // allocate 1K

	Person* person = new(storage) Person();
	person->setName( "anonymous" );
	std::cout << person->toString() << std::endl;

	// if just storage, person will be replaced by intArray. (2)
	// int* intArray = new(storage) int[10];
	int* intArray = new(storage+sizeof(person)) int[10];
	int i=0;

	// assign value.
	for( i=0; i<10; i++ )
		intArray[i] = i;

	// show the values
	for( i=0; i<10; i++ )
		printf( "%d ", intArray[i]);
	printf("\n");

	// dump storage
	char* iter=storage;
	printf("=== begin dump ===\n");
	i=0;
	while( i!=512 ) {
		printf( "%02x ", (unsigned char)*iter );
		iter++;
		i++;
		if( !(i%16) )
			printf( "\n" );
	}
	printf("=== end dump ===\n");

	printf( "person address = %08x intArray address = %08x\n",
			(unsigned int)person, (unsigned int)intArray );

	// cannot delete, it cause segmentation fault. (1)
	//delete person;
	//delete[] intArray;

	// but we can use free.
	free( storage );
}
結論:
  1. new(storage) Person() 實際上是 new 在 storage 這塊空間裡面,所以之後如果呼叫 delete,會出錯。
  2. 如果不累加 storage 的話,會把之前配置的空間覆蓋掉。
  3. malloc() 可以用 shmget()、shmat() 代替,沒有問題。
  4. 以上面的例子,std::string 會配置一塊空間來放字串,這塊空間並不在 storage 裡面,使用時要注意。如果要這樣用,應該要再取代掉 STL allocator 的機制。

Posted by elleryq at 樂多Roodo!16:05回應(2)引用(0)
標籤:c++

October 24,2008

CMake + CTest

CMake 內建 CTest,基本使用可以參考:CMake Testing With CTest

大致把要點整理如下:
  1. 在專案根目錄的 CMakeLists.txt 加上 ENABLE_TESTING()
  2. 在你 test 程式所在目錄的 CMakeLists.txt 加上 add_test( 測試名稱 執行檔名字 [參數1] [參數2] ... )
  3. 測試程式在錯誤發生時呼叫 exit() 並傳入非 0 值,正常結束的話,則呼叫 exit(0)。
  4. 大功告成以後,先刪除 CMakeCache.txt,然後用 cmake 重新產生 Makefile,接下來就可以用 make && make test 來進行測試了。
  5. make test 是進行所有測試,只想進行某幾項測試的話,可以查看 ctest 的 -R, -E, -I 這幾個選項的說明。-R 是用 regular expression 找特定名稱的測試項目,-E 則是相反,排除掉特定名稱的測試項目,-I 是指定項目號碼,表示進行指定項目的測試。


文件裡面還有提到可以把測試結果自動上傳到網站上等等,不過看來是用不到,就沒嘗試了。

Posted by elleryq at 樂多Roodo!10:13回應(0)引用(0)

September 4,2008

以 Visual Studio 2005 編譯 boost::regex

下載 boost 跟 bjam 以後,第一件事情是編譯。
  1. 打開 visual studio 2005 命令提示字元
  2. 切換到你的 boost目錄 下,這裡假設為 c:\boost_1_36_0
  3. 執行 bjam --build-dir="c:\boost_1_36_0\build" --toolset=msvc-8.0 --build-type=complete --with-regex stage,就可以只編譯 boost::regex.
  4. 編譯會需要一陣子,編譯好的檔案就會放在 c:\boost_1_36_0\build\boost\bin.v2\libs\regex\ 下


在使用時,在專案屬性裡指定 [組態屬性][C/C++][一般] 的 "其他Include目錄" 為 c:\boost_1_36_0,再指定 [組態屬性][連結器][一般] 的 "其他程式庫目錄" 為 "c:\boost_1_36_0\build\boost\bin.v2\libs\regex\build\msvc-8.0\debug\link-static\threading-multi",進行編譯即可。

Posted by elleryq at 樂多Roodo!15:21回應(0)引用(0)

May 9,2006

new之後,constructor之前

昨天在代碼裡面看到一個從沒看過的用法,如下列紅色標示部份:
#include <iostream>

class MyBase {
  protected:
    int _id;

  public:
    MyBase():_id(0) {}
    MyBase( int id ):_id(id) {}
    int getId() {return _id; }
};

class MyClass: public MyBase {
  public:
    MyClass() {}
    MyClass( int id ):MyBase( id ) {}
};

int myclass_mem[ sizeof(MyClass)/sizeof(int) ];

using namespace std;

MyBase* test()
{
  return new (myclass_mem)MyClass( 100 );// 這裡 (1)
  //return new MyClass( 100 ); // (2)
}

int
main( int argc, char* argv[] )
{
  int len = sizeof( myclass_mem );
  cout << "The size of myclass_mem is " << len << endl;
  cout << "content of myclass_mem" << endl;
  for( int i=0; i<len; i++ )
    cout << myclass_mem[i] << " ";
  cout << endl;

  MyBase* base = ::test();
  cout << base->getId() << endl;

  cout << "content of myclass_mem" << endl;
  for( int i=0; i<len; i++ )
    cout << myclass_mem[i] << " ";
  cout << endl;
}

以執行的結果來說,我實在是分辨不出來 (1) 與 (2) 有甚麼分別。
後來我用 gcc -S 去分別產生組合語言碼,總算是大致猜到了,原來以 (1) 的方法來寫,會把 new 以後的結果也複製到 myclass_mem 這個陣列裡面。 所以加上輸出 myclass_mem 內容的程式碼,再分別產生執行檔來看執行結果就很清楚了。 真是特別。

Posted by elleryq at 樂多Roodo!12:29回應(0)引用(0)
 [1]  [2]  [3]  [最終頁]