субота, 19 липня 2008 р.

Як писати справді короткі програми =)

Після переустановки Вінди треба було настроїти плагіни до топкодерівської арени. Для того, щоб потестити їх відкрив 250 другого діва з СРМ 409(останнього на даний момент).
Суть задачі полягає аж в тому, щоб порахувати кількість одиничних бітів в числі Х. Перший розвязок мав вигляд:
  1. #include <iostream>  
  2. #include <sstream>  
  3. #include <cstdio>  
  4. #include <cstdlib>  
  5. #include <cmath>  
  6. #include <memory>  
  7. #include <cctype>  
  8. #include <string>  
  9. #include <vector>  
  10. #include <list>  
  11. #include <queue>  
  12. #include <deque>  
  13. #include <stack>  
  14. #include <map>  
  15. #include <set>  
  16. #include <algorithm>  
  17. using namespace std;    
  18.   
  19. #define INF 2000000000  
  20. #define FOR(i, a, b) for(i = a; i < b; i++)  
  21. #define DFOR(i, a, b) for(i = a - 1; i >= b; i--)  
  22. #define ALL(c) (c).begin(),(c).end()   
  23.   
  24. class Stick  
  25. {   
  26. public:   
  27.  int pieces(int x)   
  28.  {   
  29.   int res=0;  
  30.   while(x>0)  
  31.   {  
  32.    if(x&1)  
  33.     res++;  
  34.    x>>=1;  
  35.   }  
  36.   return res;   
  37.  }   
  38. };   

Багато букоф... Шаблон багато займає. А якщо викинути все лишнє? Буде отак:
  1. struct Stick { int pieces(int x){ return x == 0 ? 0 : pieces(x/2)+x%2; } };  

...і все. Навіть інклудів не треба.

понеділок, 14 липня 2008 р.

Редірект з Global.asax

Писав сьогодні на роботі авторизацію на основі ролей для сайту. Так сталося, що мені треба було руцями перевіряти, чи юзер автентифікований і чи має права. Відповідно, якщо не автентифікований - то редіректити його на сторінку, де можна залогуватися, а якщо не має прав, то на сторінку, де йому скажуть "Access denied". Ніби все просто: вішаємо в Global.asax на подію AuthorizeRequest обробник, у якому перевіряємо права юзера і редіректимо його куди треба. Якось от так:
  1. protected void Application_Start(object sender, EventArgs e)  
  2. {  
  3.     AuthorizeRequest += Application_AutorizeRequest;  
  4. }  
  5.   
  6. protected void Application_AutorizeRequest(object sender, EventArgs e)  
  7. {  
  8.     if (Request.IsAuthenticated)  
  9.     {  
  10.         if(...) // user hasn`t rights to use current page  
  11.         {  
  12.             Response.Redirect("AccessDenied.aspx");  
  13.         }  
  14.     }  
  15.     else // must log in  
  16.     {  
  17.         Response.Redirect("Login.aspx");  
  18.     }  
  19. }  


Компілимо код, запускаємо, пробуємо і... Ніфіга не працює! Мало того, після наших редіректів аплікація взагалі вмирає - браузер приречено повідомляє про те, що не може отримати сторінку від сервера. Оживає все тільки по F5.
В чому ж проблема?
По перше, давайте зрозуміємо, що робить Response.Redirect. Він записує кудись адресу, куди треба перенаправити браузер, після чого викидає System.Threading.ThreadAbortException, який перериває виконання поточного потоку. Біда в тому, що на етапі, коли обробляються події в Global.asax ще не існує механізму, який перенаправить браузер за адресою, яку залишив Response.Redirect. Власне тому виклик цього методу просто і тупо завершує роботу аплікації.
ОК. А що ж робити? А все просто.

/*
Тут маленький відступ: хорошим тоном в ASP.Net є наслідування всіх стандартних контролів своїми, в які в будь-який момент можна дописати все, що завгодно. Так нехай всі наші сторінки наслідують не стандартний System.Web.UI.Page, а свій public class MyPage : System.Web.UI.Page.
*/

Отже, допишемо в Global.asax метод, який запише в кеші адресу сторінки, куди треба редіректитися і заюзаємо його в обробнику Application_AutorizeRequest:
  1. protected void Application_Start(object sender, EventArgs e)  
  2. {  
  3.     AuthorizeRequest += Application_AutorizeRequest;  
  4. }  
  5.   
  6. protected void Application_AutorizeRequest(object sender, EventArgs e)  
  7. {  
  8.     if (Request.IsAuthenticated)  
  9.     {  
  10.         if(...) // user hasn`t rights to use current page  
  11.         {  
  12.             Redirect("AccessDenied.aspx");  
  13.         }  
  14.     }  
  15.     else  
  16.     {  
  17.         Redirect("Login.aspx");  
  18.     }  
  19. }  
  20.   
  21. protected void Redirect(string url)  
  22. {  
  23.     HttpContext.Current.Cache["RedirectUrl"] = url;  
  24. }  

А тепер треба сказати сторінці, що треба редіректитися, якщо в кеші є ця адреса:
  1. public abstract class MyPage : System.Web.UI.Page  
  2. {  
  3.     protected void Page_Load(object sender, EventArgs e)  
  4.     {  
  5.         if(HttpContext.Current.Cache["RedirectUrl"] != null)  
  6.         {  
  7.             string url = (string) HttpContext.Current.Cache["RedirectUrl"];  
  8.             HttpContext.Current.Cache.Remove("RedirectUrl");  
  9.             Response.Redirect(url);  
  10.         }  
  11.         // ...   
  12.     }  
  13.     // ...  
  14. }  

Завдяки тому, що ми унаслідували свій клас для сторінки, не потрібно писати одне й те ж для кожної сторінки - тепер все прекрасно працює.
Отака повчальна історія.
P.S. Нарешті знайшов нормальний syntax highlighter для блогспота. Чим і хвалюся =)